1 /*
2  * Copyright 2016 Google Inc.
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 #ifndef SKSL_PROGRAM
9 #define SKSL_PROGRAM
10 
11 #include <vector>
12 #include <memory>
13 
14 #include "include/private/SkTHash.h"
15 #include "src/sksl/SkSLAnalysis.h"
16 #include "src/sksl/ir/SkSLBoolLiteral.h"
17 #include "src/sksl/ir/SkSLExpression.h"
18 #include "src/sksl/ir/SkSLFloatLiteral.h"
19 #include "src/sksl/ir/SkSLIntLiteral.h"
20 #include "src/sksl/ir/SkSLModifiers.h"
21 #include "src/sksl/ir/SkSLProgramElement.h"
22 #include "src/sksl/ir/SkSLSymbolTable.h"
23 
24 #ifdef SK_VULKAN
25 #include "src/gpu/vk/GrVkCaps.h"
26 #endif
27 
28 // name of the render target width uniform
29 #define SKSL_RTWIDTH_NAME "u_skRTWidth"
30 
31 // name of the render target height uniform
32 #define SKSL_RTHEIGHT_NAME "u_skRTHeight"
33 
34 namespace SkSL {
35 
36 class Context;
37 class Pool;
38 
39 /**
40  * Side-car class holding mutable information about a Program's IR
41  */
42 class ProgramUsage {
43 public:
44     struct VariableCounts { int fRead = 0; int fWrite = 0; };
45     VariableCounts get(const Variable&) const;
46     bool isDead(const Variable&) const;
47 
48     int get(const FunctionDeclaration&) const;
49 
50     void replace(const Expression* oldExpr, const Expression* newExpr);
51     void add(const Statement* stmt);
52     void remove(const Expression* expr);
53     void remove(const Statement* stmt);
54     void remove(const ProgramElement& element);
55 
56     SkTHashMap<const Variable*, VariableCounts> fVariableCounts;
57     SkTHashMap<const FunctionDeclaration*, int> fCallCounts;
58 };
59 
60 /**
61  * Represents a fully-digested program, ready for code generation.
62  */
63 struct Program {
64     struct Settings {
65         struct Value {
ValueProgram::Settings::Value66             Value(bool b)
67             : fKind(kBool_Kind)
68             , fValue(b) {}
69 
ValueProgram::Settings::Value70             Value(int i)
71             : fKind(kInt_Kind)
72             , fValue(i) {}
73 
ValueProgram::Settings::Value74             Value(unsigned int i)
75             : fKind(kInt_Kind)
76             , fValue(i) {}
77 
ValueProgram::Settings::Value78             Value(float f)
79             : fKind(kFloat_Kind)
80             , fValueF(f) {}
81 
literalProgram::Settings::Value82             std::unique_ptr<Expression> literal(const Context& context, int offset) const {
83                 switch (fKind) {
84                     case Program::Settings::Value::kBool_Kind:
85                         return std::unique_ptr<Expression>(new BoolLiteral(context,
86                                                                            offset,
87                                                                            fValue));
88                     case Program::Settings::Value::kInt_Kind:
89                         return std::unique_ptr<Expression>(new IntLiteral(context,
90                                                                           offset,
91                                                                           fValue));
92                     case Program::Settings::Value::kFloat_Kind:
93                         return std::unique_ptr<Expression>(new FloatLiteral(context,
94                                                                             offset,
95                                                                             fValueF));
96                     default:
97                         SkASSERT(false);
98                         return nullptr;
99                 }
100             }
101 
102             enum {
103                 kBool_Kind,
104                 kInt_Kind,
105                 kFloat_Kind,
106             } fKind;
107 
108             union {
109                 int   fValue;  // for kBool_Kind and kInt_Kind
110                 float fValueF; // for kFloat_Kind
111             };
112         };
113 
114         // if false, sk_FragCoord is exactly the same as gl_FragCoord. If true, the y coordinate
115         // must be flipped.
116         bool fFlipY = false;
117         // if false, sk_FragCoord is exactly the same as gl_FragCoord. If true, the w coordinate
118         // must be inversed.
119         bool fInverseW = false;
120         // If true the destination fragment color is read sk_FragColor. It must be declared inout.
121         bool fFragColorIsInOut = false;
122         // if true, Setting objects (e.g. sk_Caps.fbFetchSupport) should be replaced with their
123         // constant equivalents during compilation
124         bool fReplaceSettings = true;
125         // if true, all halfs are forced to be floats
126         bool fForceHighPrecision = false;
127         // if true, add -0.5 bias to LOD of all texture lookups
128         bool fSharpenTextures = false;
129         // if the program needs to create an RTHeight uniform, this is its offset in the uniform
130         // buffer
131         int fRTHeightOffset = -1;
132         // if the program needs to create an RTHeight uniform and is creating spriv, this is the
133         // binding and set number of the uniform buffer.
134         int fRTHeightBinding = -1;
135         int fRTHeightSet = -1;
136         // If true, remove any uncalled functions other than main(). Note that a function which
137         // starts out being used may end up being uncalled after optimization.
138         bool fRemoveDeadFunctions = true;
139         // Functions larger than this (measured in IR nodes) will not be inlined. The default value
140         // is arbitrary. A value of zero will disable the inliner entirely.
141         int fInlineThreshold = 49;
142         // true to enable optimization passes
143         bool fOptimize = true;
144         // If true, implicit conversions to lower precision numeric types are allowed
145         // (eg, float to half)
146         bool fAllowNarrowingConversions = false;
147         // If true, then Debug code will run SPIR-V output through the validator to ensure its
148         // correctness
149         bool fValidateSPIRV = true;
150     };
151 
152     struct Inputs {
153         // if true, this program requires the render target width uniform to be defined
154         bool fRTWidth;
155 
156         // if true, this program requires the render target height uniform to be defined
157         bool fRTHeight;
158 
159         // if true, this program must be recompiled if the flipY setting changes. If false, the
160         // program will compile to the same code regardless of the flipY setting.
161         bool fFlipY;
162 
resetProgram::Inputs163         void reset() {
164             fRTWidth = false;
165             fRTHeight = false;
166             fFlipY = false;
167         }
168 
isEmptyProgram::Inputs169         bool isEmpty() {
170             return !fRTWidth && !fRTHeight && !fFlipY;
171         }
172     };
173 
174     enum Kind {
175         kFragment_Kind,
176         kVertex_Kind,
177         kGeometry_Kind,
178         kFragmentProcessor_Kind,
179         kPipelineStage_Kind,
180         kGeneric_Kind,
181     };
182 
ProgramProgram183     Program(Kind kind,
184             std::unique_ptr<String> source,
185             Settings settings,
186             const ShaderCapsClass* caps,
187             std::shared_ptr<Context> context,
188             std::vector<std::unique_ptr<ProgramElement>> elements,
189             std::unique_ptr<ModifiersPool> modifiers,
190             std::shared_ptr<SymbolTable> symbols,
191             std::unique_ptr<Pool> pool,
192             Inputs inputs)
193     : fKind(kind)
194     , fSource(std::move(source))
195     , fSettings(settings)
196     , fCaps(caps)
197     , fContext(context)
198     , fSymbols(symbols)
199     , fPool(std::move(pool))
200     , fInputs(inputs)
201     , fElements(std::move(elements))
202     , fModifiers(std::move(modifiers)) {
203         fUsage = Analysis::GetUsage(*this);
204     }
205 
~ProgramProgram206     ~Program() {
207         // Some or all of the program elements are in the pool. To free them safely, we must attach
208         // the pool before destroying any program elements. (Otherwise, we may accidentally call
209         // delete on a pooled node.)
210         fPool->attachToThread();
211         fElements.clear();
212         fContext.reset();
213         fSymbols.reset();
214         fModifiers.reset();
215         fPool->detachFromThread();
216     }
217 
elementsProgram218     const std::vector<std::unique_ptr<ProgramElement>>& elements() const { return fElements; }
219 
220     Kind fKind;
221     std::unique_ptr<String> fSource;
222     Settings fSettings;
223     const ShaderCapsClass* fCaps;
224     std::shared_ptr<Context> fContext;
225     // it's important to keep fElements defined after (and thus destroyed before) fSymbols,
226     // because destroying elements can modify reference counts in symbols
227     std::shared_ptr<SymbolTable> fSymbols;
228     std::unique_ptr<Pool> fPool;
229     Inputs fInputs;
230 
231 private:
232     std::vector<std::unique_ptr<ProgramElement>> fElements;
233     std::unique_ptr<ModifiersPool> fModifiers;
234     std::unique_ptr<ProgramUsage> fUsage;
235 
236     friend class Compiler;
237     friend class Inliner;             // fUsage
238     friend class SPIRVCodeGenerator;  // fModifiers
239 };
240 
241 }  // namespace SkSL
242 
243 #endif
244