1 //
2 // Copyright 2021 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 // EmulateFragColorData: Emulate gl_FragColor and gl_FragData.
7 //
8 
9 #include "compiler/translator/tree_ops/vulkan/EmulateFragColorData.h"
10 
11 #include "compiler/translator/Compiler.h"
12 #include "compiler/translator/ImmutableStringBuilder.h"
13 #include "compiler/translator/StaticType.h"
14 #include "compiler/translator/SymbolTable.h"
15 #include "compiler/translator/tree_util/IntermNode_util.h"
16 #include "compiler/translator/tree_util/IntermTraverse.h"
17 #include "compiler/translator/tree_util/ReplaceVariable.h"
18 
19 namespace sh
20 {
21 namespace
22 {
23 // Traverser that:
24 //
25 // 1. Declares outputs corresponding to gl_FragColor and gl_FragData (and their corresponding
26 //    Secondary versions for framebuffer fetch).
27 // 2. Replaces built-in references with these variables.
28 class EmulateFragColorDataTraverser : public TIntermTraverser
29 {
30   public:
EmulateFragColorDataTraverser(TCompiler * compiler,TSymbolTable * symbolTable)31     EmulateFragColorDataTraverser(TCompiler *compiler, TSymbolTable *symbolTable)
32         : TIntermTraverser(true, false, false, symbolTable), mResources(compiler->getResources())
33     {}
34 
visitSymbol(TIntermSymbol * symbol)35     void visitSymbol(TIntermSymbol *symbol) override
36     {
37         const TVariable *variable = &symbol->variable();
38         const TType &type         = variable->getType();
39 
40         // If this built-in was already visited, reuse the variable defined for it.
41         auto replacement = mVariableMap.find(variable);
42         if (replacement != mVariableMap.end())
43         {
44             queueReplacement(replacement->second->deepCopy(), OriginalNode::IS_DROPPED);
45             return;
46         }
47 
48         unsigned int arraySize = 0;
49         int index              = 0;
50         const char *name       = "";
51 
52         // Replace the built-ins being emulated with a variable of the appropriate type.
53         switch (type.getQualifier())
54         {
55             case EvqFragColor:
56                 name = "webgl_FragColor";
57                 break;
58             case EvqFragData:
59                 name      = "webgl_FragData";
60                 arraySize = mResources.MaxDrawBuffers;
61                 break;
62             case EvqSecondaryFragColorEXT:
63                 name  = "webgl_SecondaryFragColor";
64                 index = 1;
65                 break;
66             case EvqSecondaryFragDataEXT:
67                 name      = "webgl_SecondaryFragData";
68                 index     = 1;
69                 arraySize = mResources.MaxDualSourceDrawBuffers;
70                 break;
71             default:
72                 // Not the built-in we are looking for.
73                 return;
74         }
75 
76         TType *outputType = new TType(*StaticType::GetQualified<EbtFloat, EvqFragmentOut, 4, 1>());
77         if (arraySize > 0)
78         {
79             outputType->makeArray(arraySize);
80         }
81         if (index > 0)
82         {
83             TLayoutQualifier layoutQualifier = outputType->getLayoutQualifier();
84             layoutQualifier.index            = index;
85             outputType->setLayoutQualifier(layoutQualifier);
86         }
87 
88         TVariable *replacementVar = new TVariable(mSymbolTable, ImmutableString(name), outputType,
89                                                   SymbolType::AngleInternal);
90 
91         TIntermSymbol *newSymbol = new TIntermSymbol(replacementVar);
92         mVariableMap[variable]   = newSymbol;
93 
94         queueReplacement(newSymbol, OriginalNode::IS_DROPPED);
95     }
96 
addDeclarations(TIntermBlock * root)97     void addDeclarations(TIntermBlock *root)
98     {
99         // Insert the declaration before the first function.
100         size_t firstFunctionIndex = FindFirstFunctionDefinitionIndex(root);
101         TIntermSequence declarations;
102 
103         for (auto &replaced : mVariableMap)
104         {
105             TIntermDeclaration *decl = new TIntermDeclaration;
106             TIntermSymbol *symbol    = replaced.second->deepCopy()->getAsSymbolNode();
107             decl->appendDeclarator(symbol);
108             declarations.push_back(decl);
109         }
110 
111         root->insertChildNodes(firstFunctionIndex, declarations);
112     }
113 
114   private:
115     const ShBuiltInResources &mResources;
116 
117     // A map of already replaced built-in variables.
118     VariableReplacementMap mVariableMap;
119 };
120 }  // anonymous namespace
121 
EmulateFragColorData(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable)122 bool EmulateFragColorData(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable)
123 {
124     if (compiler->getShaderType() != GL_FRAGMENT_SHADER)
125     {
126         return true;
127     }
128 
129     EmulateFragColorDataTraverser traverser(compiler, symbolTable);
130     root->traverse(&traverser);
131     if (!traverser.updateTree(compiler, root))
132     {
133         return false;
134     }
135 
136     traverser.addDeclarations(root);
137     return compiler->validateAST(root);
138 }
139 }  // namespace sh
140