xref: /aosp_15_r20/external/skia/src/sksl/transform/SkSLFindAndDeclareBuiltinVariables.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2021 Google LLC.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkSpan.h"
9 #include "include/core/SkTypes.h"
10 #include "src/core/SkTHash.h"
11 #include "src/sksl/SkSLBuiltinTypes.h"
12 #include "src/sksl/SkSLCompiler.h"
13 #include "src/sksl/SkSLContext.h"
14 #include "src/sksl/SkSLProgramSettings.h"
15 #include "src/sksl/analysis/SkSLProgramUsage.h"
16 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
17 #include "src/sksl/ir/SkSLFunctionDefinition.h"
18 #include "src/sksl/ir/SkSLIRNode.h"
19 #include "src/sksl/ir/SkSLInterfaceBlock.h"
20 #include "src/sksl/ir/SkSLLayout.h"
21 #include "src/sksl/ir/SkSLProgram.h"
22 #include "src/sksl/ir/SkSLProgramElement.h"
23 #include "src/sksl/ir/SkSLSymbol.h"
24 #include "src/sksl/ir/SkSLSymbolTable.h"
25 #include "src/sksl/ir/SkSLType.h"
26 #include "src/sksl/ir/SkSLVarDeclarations.h"
27 #include "src/sksl/ir/SkSLVariable.h"
28 #include "src/sksl/transform/SkSLTransform.h"
29 
30 #include <algorithm>
31 #include <memory>
32 #include <string_view>
33 #include <vector>
34 
35 namespace SkSL {
36 namespace Transform {
37 namespace {
38 
39 class BuiltinVariableScanner {
40 public:
BuiltinVariableScanner(const Context & context,const SymbolTable & symbols)41     BuiltinVariableScanner(const Context& context, const SymbolTable& symbols)
42             : fContext(context)
43             , fSymbols(symbols) {}
44 
addDeclaringElement(const ProgramElement * decl)45     void addDeclaringElement(const ProgramElement* decl) {
46         // Make sure we only add a built-in variable once. We only have a small handful of built-in
47         // variables to declare, so linear search here is good enough.
48         if (std::find(fNewElements.begin(), fNewElements.end(), decl) == fNewElements.end()) {
49             fNewElements.push_back(decl);
50         }
51     }
52 
addDeclaringElement(const Symbol * symbol)53     void addDeclaringElement(const Symbol* symbol) {
54         if (!symbol || !symbol->is<Variable>()) {
55             return;
56         }
57         const Variable& var = symbol->as<Variable>();
58         if (const GlobalVarDeclaration* decl = var.globalVarDeclaration()) {
59             this->addDeclaringElement(decl);
60         } else if (const InterfaceBlock* block = var.interfaceBlock()) {
61             this->addDeclaringElement(block);
62         } else {
63             // Double-check that this variable isn't associated with a global or an interface block.
64             // (Locals and parameters will come along naturally as part of the associated function.)
65             SkASSERTF(var.storage() != VariableStorage::kGlobal &&
66                       var.storage() != VariableStorage::kInterfaceBlock,
67                       "%.*s", (int)var.name().size(), var.name().data());
68         }
69     }
70 
addImplicitFragColorWrite(SkSpan<const std::unique_ptr<ProgramElement>> elements)71     void addImplicitFragColorWrite(SkSpan<const std::unique_ptr<ProgramElement>> elements) {
72         for (const std::unique_ptr<ProgramElement>& pe : elements) {
73             if (!pe->is<FunctionDefinition>()) {
74                 continue;
75             }
76             const FunctionDefinition& funcDef = pe->as<FunctionDefinition>();
77             if (funcDef.declaration().isMain()) {
78                 if (funcDef.declaration().returnType().matches(*fContext.fTypes.fHalf4)) {
79                     // We synthesize writes to sk_FragColor if main() returns a color, even if it's
80                     // otherwise unreferenced.
81                     this->addDeclaringElement(fSymbols.findBuiltinSymbol(Compiler::FRAGCOLOR_NAME));
82                 }
83                 // Now that main() has been found, we can stop scanning.
84                 break;
85             }
86         }
87     }
88 
GlobalVarBuiltinName(const ProgramElement & elem)89     static std::string_view GlobalVarBuiltinName(const ProgramElement& elem) {
90         return elem.as<GlobalVarDeclaration>().varDeclaration().var()->name();
91     }
92 
InterfaceBlockName(const ProgramElement & elem)93     static std::string_view InterfaceBlockName(const ProgramElement& elem) {
94         return elem.as<InterfaceBlock>().instanceName();
95     }
96 
sortNewElements()97     void sortNewElements() {
98         std::sort(fNewElements.begin(),
99                   fNewElements.end(),
100                   [](const ProgramElement* a, const ProgramElement* b) {
101                       if (a->kind() != b->kind()) {
102                           return a->kind() < b->kind();
103                       }
104                       switch (a->kind()) {
105                           case ProgramElement::Kind::kGlobalVar:
106                               SkASSERT(a == b ||
107                                        GlobalVarBuiltinName(*a) != GlobalVarBuiltinName(*b));
108                               return GlobalVarBuiltinName(*a) < GlobalVarBuiltinName(*b);
109 
110                           case ProgramElement::Kind::kInterfaceBlock:
111                               SkASSERT(a == b || InterfaceBlockName(*a) != InterfaceBlockName(*b));
112                               return InterfaceBlockName(*a) < InterfaceBlockName(*b);
113 
114                           default:
115                               SkUNREACHABLE;
116                       }
117                   });
118     }
119 
120     const Context& fContext;
121     const SymbolTable& fSymbols;
122     std::vector<const ProgramElement*> fNewElements;
123 };
124 
125 }  // namespace
126 
FindAndDeclareBuiltinVariables(Program & program)127 void FindAndDeclareBuiltinVariables(Program& program) {
128     using Interface = Program::Interface;
129     const Context& context = *program.fContext;
130     const SymbolTable& symbols = *program.fSymbols;
131     BuiltinVariableScanner scanner(context, symbols);
132 
133     if (ProgramConfig::IsFragment(program.fConfig->fKind)) {
134         // Find main() in the program and check its return type.
135         // If it's half4, we treat that as an implicit write to sk_FragColor and add a reference.
136         scanner.addImplicitFragColorWrite(program.fOwnedElements);
137     }
138 
139     // Scan all the variables used by the program and declare any built-ins.
140     for (const auto& [var, counts] : program.fUsage->fVariableCounts) {
141         if (var->isBuiltin()) {
142             scanner.addDeclaringElement(var);
143 
144             switch (var->layout().fBuiltin) {
145                 // Set the RTFlip program input if we find sk_FragCoord or sk_Clockwise.
146                 case SK_FRAGCOORD_BUILTIN:
147                     if (!context.fConfig->fSettings.fForceNoRTFlip) {
148                         program.fInterface.fRTFlipUniform |= Interface::kRTFlip_FragCoord;
149                     }
150                     break;
151 
152                 case SK_CLOCKWISE_BUILTIN:
153                     if (!context.fConfig->fSettings.fForceNoRTFlip) {
154                         program.fInterface.fRTFlipUniform |= Interface::kRTFlip_Clockwise;
155                     }
156                     break;
157 
158                 // Set the UseLastFragColor program input if we find sk_LastFragColor.
159                 // Metal and Dawn define this as a program input, rather than a global variable.
160                 case SK_LASTFRAGCOLOR_BUILTIN:
161                     program.fInterface.fUseLastFragColor = true;
162                     break;
163 
164                 // Set secondary color output if we find sk_SecondaryFragColor.
165                 case SK_SECONDARYFRAGCOLOR_BUILTIN:
166                     program.fInterface.fOutputSecondaryColor = true;
167                     break;
168             }
169         }
170     }
171 
172     // Sort the referenced builtin functions into a consistent order; otherwise our output will
173     // become non-deterministic. The exact order isn't particularly important.
174     scanner.sortNewElements();
175 
176     // Add all the newly-declared elements to the program, and update ProgramUsage to match.
177     program.fSharedElements.insert(program.fSharedElements.begin(),
178                                    scanner.fNewElements.begin(),
179                                    scanner.fNewElements.end());
180 
181     for (const ProgramElement* element : scanner.fNewElements) {
182         program.fUsage->add(*element);
183     }
184 }
185 
186 }  // namespace Transform
187 }  // namespace SkSL
188