blob: 259662b62719e9bcabda455085e58285f14d7c1d [file] [log] [blame]
Ethan Nicholas1e6e9192021-09-30 12:47:04 -04001/*
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 "src/sksl/transform/SkSLTransform.h"
9
10#include "src/sksl/SkSLAnalysis.h"
11#include "src/sksl/SkSLCompiler.h"
12#include "src/sksl/SkSLContext.h"
13#include "src/sksl/SkSLIntrinsicMap.h"
14#include "src/sksl/dsl/priv/DSLWriter.h"
15#include "src/sksl/ir/SkSLFunctionDefinition.h"
16#include "src/sksl/ir/SkSLInterfaceBlock.h"
17#include "src/sksl/ir/SkSLVarDeclarations.h"
18
19namespace SkSL {
20
21namespace Transform {
22
23void FindAndDeclareBuiltinVariables(const Context& context,
24 ProgramKind programKind, std::vector<const ProgramElement*>& sharedElements) {
25 class BuiltinVariableScanner : public ProgramVisitor {
26 public:
27 BuiltinVariableScanner(const Context& context)
28 : fContext(context) {}
29
30 void addDeclaringElement(const String& name) {
31 // If this is the *first* time we've seen this builtin, findAndInclude will return
32 // the corresponding ProgramElement.
33 IntrinsicMap& intrinsics = *fContext.fIntrinsics;
34 if (const ProgramElement* decl = intrinsics.findAndInclude(name)) {
35 SkASSERT(decl->is<GlobalVarDeclaration>() || decl->is<InterfaceBlock>());
36 fNewElements.push_back(decl);
37 }
38 }
39
40 bool visitProgramElement(const ProgramElement& pe) override {
41 if (pe.is<FunctionDefinition>()) {
42 const FunctionDefinition& funcDef = pe.as<FunctionDefinition>();
43 // We synthesize writes to sk_FragColor if main() returns a color, even if it's
44 // otherwise unreferenced. Check main's return type to see if it's half4.
45 if (funcDef.declaration().isMain() &&
46 funcDef.declaration().returnType() == *fContext.fTypes.fHalf4) {
47 fPreserveFragColor = true;
48 }
49 }
50 return INHERITED::visitProgramElement(pe);
51 }
52
53 bool visitExpression(const Expression& e) override {
54 if (e.is<VariableReference>() && e.as<VariableReference>().variable()->isBuiltin()) {
55 this->addDeclaringElement(String(e.as<VariableReference>().variable()->name()));
56 }
57 return INHERITED::visitExpression(e);
58 }
59
60 const Context& fContext;
61 std::vector<const ProgramElement*> fNewElements;
62 bool fPreserveFragColor = false;
63
64 using INHERITED = ProgramVisitor;
65 using INHERITED::visitProgramElement;
66 };
67
68 BuiltinVariableScanner scanner(context);
69 for (auto& e : dsl::DSLWriter::ProgramElements()) {
70 scanner.visitProgramElement(*e);
71 }
72
73 if (scanner.fPreserveFragColor) {
74 // main() returns a half4, so make sure we don't dead-strip sk_FragColor.
75 scanner.addDeclaringElement(Compiler::FRAGCOLOR_NAME);
76 }
77
78 switch (programKind) {
79 case ProgramKind::kFragment:
80 // Vulkan requires certain builtin variables be present, even if they're unused. At one
81 // time, validation errors would result if sk_Clockwise was missing. Now, it's just
82 // (Adreno) driver bugs that drop or corrupt draws if they're missing.
83 scanner.addDeclaringElement("sk_Clockwise");
84 break;
85 default:
86 break;
87 }
88
89 sharedElements.insert(sharedElements.begin(), scanner.fNewElements.begin(),
90 scanner.fNewElements.end());
91}
92
93} // namespace Transform
94
95} // namespace SkSL