1 /*
2 * Copyright 2022 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 #include "include/core/SkSpan.h"
9 #include "src/sksl/SkSLBuiltinTypes.h"
10 #include "src/sksl/SkSLCompiler.h"
11 #include "src/sksl/SkSLContext.h"
12 #include "src/sksl/SkSLErrorReporter.h"
13 #include "src/sksl/SkSLProgramSettings.h"
14 #include "src/sksl/SkSLString.h"
15 #include "src/sksl/ir/SkSLFieldSymbol.h"
16 #include "src/sksl/ir/SkSLInterfaceBlock.h"
17 #include "src/sksl/ir/SkSLLayout.h"
18 #include "src/sksl/ir/SkSLModifierFlags.h"
19 #include "src/sksl/ir/SkSLModifiers.h"
20 #include "src/sksl/ir/SkSLSymbolTable.h"
21 #include "src/sksl/ir/SkSLVarDeclarations.h"
22
23 #include <cstddef>
24 #include <cstdint>
25 #include <optional>
26 #include <utility>
27
28 using namespace skia_private;
29
30 namespace SkSL {
31
32 enum class ProgramKind : int8_t;
33
~InterfaceBlock()34 InterfaceBlock::~InterfaceBlock() {
35 // Unhook this InterfaceBlock from its associated Variable, since we're being deleted.
36 if (fVariable) {
37 fVariable->detachDeadInterfaceBlock();
38 }
39 }
40
find_rt_adjust_index(SkSpan<const Field> fields)41 static std::optional<int> find_rt_adjust_index(SkSpan<const Field> fields) {
42 for (size_t index = 0; index < fields.size(); ++index) {
43 const SkSL::Field& f = fields[index];
44 if (f.fName == SkSL::Compiler::RTADJUST_NAME) {
45 return index;
46 }
47 }
48
49 return std::nullopt;
50 }
51
Convert(const Context & context,Position pos,const Modifiers & modifiers,std::string_view typeName,TArray<Field> fields,std::string_view varName,int arraySize)52 std::unique_ptr<InterfaceBlock> InterfaceBlock::Convert(const Context& context,
53 Position pos,
54 const Modifiers& modifiers,
55 std::string_view typeName,
56 TArray<Field> fields,
57 std::string_view varName,
58 int arraySize) {
59 if (SkSL::ProgramKind kind = context.fConfig->fKind; !ProgramConfig::IsFragment(kind) &&
60 !ProgramConfig::IsVertex(kind) &&
61 !ProgramConfig::IsCompute(kind)) {
62 context.fErrors->error(pos, "interface blocks are not allowed in this kind of program");
63 return nullptr;
64 }
65 // Find sk_RTAdjust and error out if it's not of type `float4`.
66 std::optional<int> rtAdjustIndex = find_rt_adjust_index(fields);
67 if (rtAdjustIndex.has_value()) {
68 const Field& rtAdjustField = fields[*rtAdjustIndex];
69 if (!rtAdjustField.fType->matches(*context.fTypes.fFloat4)) {
70 context.fErrors->error(rtAdjustField.fPosition, "sk_RTAdjust must have type 'float4'");
71 return nullptr;
72 }
73 }
74 // Build a struct type corresponding to the passed-in fields.
75 const Type* baseType = context.fSymbolTable->add(context,
76 Type::MakeStructType(context,
77 pos,
78 typeName,
79 std::move(fields),
80 /*interfaceBlock=*/true));
81 // Array-ify the type if necessary.
82 const Type* type = baseType;
83 if (arraySize > 0) {
84 arraySize = type->convertArraySize(context, pos, pos, arraySize);
85 if (!arraySize) {
86 return nullptr;
87 }
88 type = context.fSymbolTable->addArrayDimension(context, type, arraySize);
89 }
90
91 // Error-check the interface block as if it were being declared as a global variable.
92 VarDeclaration::ErrorCheck(context,
93 pos,
94 modifiers.fPosition,
95 modifiers.fLayout,
96 modifiers.fFlags,
97 type,
98 baseType,
99 VariableStorage::kGlobal);
100
101 // Create a global variable for the Interface Block.
102 std::unique_ptr<SkSL::Variable> var = SkSL::Variable::Convert(context,
103 pos,
104 modifiers.fPosition,
105 modifiers.fLayout,
106 modifiers.fFlags,
107 type,
108 pos,
109 varName,
110 VariableStorage::kGlobal);
111 return InterfaceBlock::Make(context,
112 pos,
113 context.fSymbolTable->takeOwnershipOfSymbol(std::move(var)));
114 }
115
Make(const Context & context,Position pos,Variable * variable)116 std::unique_ptr<InterfaceBlock> InterfaceBlock::Make(const Context& context,
117 Position pos,
118 Variable* variable) {
119 SkASSERT(ProgramConfig::IsFragment(context.fConfig->fKind) ||
120 ProgramConfig::IsVertex(context.fConfig->fKind) ||
121 ProgramConfig::IsCompute(context.fConfig->fKind));
122
123 SkASSERT(variable->type().componentType().isInterfaceBlock());
124 SkSpan<const Field> fields = variable->type().componentType().fields();
125
126 if (variable->name().empty()) {
127 // This interface block is anonymous. Add each field to the top-level symbol table.
128 for (size_t i = 0; i < fields.size(); ++i) {
129 context.fSymbolTable->add(
130 context, std::make_unique<SkSL::FieldSymbol>(fields[i].fPosition, variable, i));
131 }
132 } else {
133 // Add the global variable to the top-level symbol table.
134 context.fSymbolTable->addWithoutOwnership(context, variable);
135 }
136
137 return std::make_unique<SkSL::InterfaceBlock>(pos, variable);
138 }
139
description() const140 std::string InterfaceBlock::description() const {
141 std::string result = this->var()->layout().description() +
142 this->var()->modifierFlags().description() + ' ' +
143 std::string(this->typeName()) + " {\n";
144 const Type* structType = &this->var()->type();
145 if (structType->isArray()) {
146 structType = &structType->componentType();
147 }
148 for (const auto& f : structType->fields()) {
149 result += f.description() + "\n";
150 }
151 result += "}";
152 if (!this->instanceName().empty()) {
153 result += " " + std::string(this->instanceName());
154 if (this->arraySize() > 0) {
155 String::appendf(&result, "[%d]", this->arraySize());
156 }
157 }
158 return result + ";";
159 }
160
161 } // namespace SkSL
162