xref: /aosp_15_r20/external/skia/src/sksl/ir/SkSLVarDeclarations.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2021 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVarDeclarations.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkEnumBitMask.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLAnalysis.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLBuiltinTypes.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLCompiler.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLContext.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLErrorReporter.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLPosition.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramKind.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramSettings.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLString.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLayout.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifierFlags.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifiers.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSymbolTable.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLType.h"
27*c8dee2aaSAndroid Build Coastguard Worker 
28*c8dee2aaSAndroid Build Coastguard Worker #include <string_view>
29*c8dee2aaSAndroid Build Coastguard Worker 
30*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
31*c8dee2aaSAndroid Build Coastguard Worker namespace {
32*c8dee2aaSAndroid Build Coastguard Worker 
check_valid_uniform_type(Position pos,const Type * t,const Context & context,bool topLevel=true)33*c8dee2aaSAndroid Build Coastguard Worker static bool check_valid_uniform_type(Position pos,
34*c8dee2aaSAndroid Build Coastguard Worker                                      const Type* t,
35*c8dee2aaSAndroid Build Coastguard Worker                                      const Context& context,
36*c8dee2aaSAndroid Build Coastguard Worker                                      bool topLevel = true) {
37*c8dee2aaSAndroid Build Coastguard Worker     auto reportError = [&]() {
38*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "variables of type '" + t->displayName() +
39*c8dee2aaSAndroid Build Coastguard Worker                                     "' may not be uniform");
40*c8dee2aaSAndroid Build Coastguard Worker     };
41*c8dee2aaSAndroid Build Coastguard Worker 
42*c8dee2aaSAndroid Build Coastguard Worker     // In Runtime Effects we only allow a restricted set of types: shader, blender, colorFilter,
43*c8dee2aaSAndroid Build Coastguard Worker     // 32-bit signed integers, 16-bit and 32-bit floats, and their vector/square-matrix composites.
44*c8dee2aaSAndroid Build Coastguard Worker     if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
45*c8dee2aaSAndroid Build Coastguard Worker         // `shader`, `blender`, `colorFilter`
46*c8dee2aaSAndroid Build Coastguard Worker         if (t->isEffectChild()) {
47*c8dee2aaSAndroid Build Coastguard Worker             return true;
48*c8dee2aaSAndroid Build Coastguard Worker         }
49*c8dee2aaSAndroid Build Coastguard Worker 
50*c8dee2aaSAndroid Build Coastguard Worker         // `int`, `int2`, `int3`, `int4`
51*c8dee2aaSAndroid Build Coastguard Worker         const Type& ct = t->componentType();
52*c8dee2aaSAndroid Build Coastguard Worker         if (ct.isSigned() && ct.bitWidth() == 32 && (t->isScalar() || t->isVector())) {
53*c8dee2aaSAndroid Build Coastguard Worker             return true;
54*c8dee2aaSAndroid Build Coastguard Worker         }
55*c8dee2aaSAndroid Build Coastguard Worker 
56*c8dee2aaSAndroid Build Coastguard Worker         // `float`, `float2`, `float3`, `float4`, `float2x2`, `float3x3`, `float4x4`
57*c8dee2aaSAndroid Build Coastguard Worker         // `half`, `half2`, `half3`, `half4`, `half2x2`, `half3x3`, `half4x4`
58*c8dee2aaSAndroid Build Coastguard Worker         if (ct.isFloat() &&
59*c8dee2aaSAndroid Build Coastguard Worker             (t->isScalar() || t->isVector() || (t->isMatrix() && t->rows() == t->columns()))) {
60*c8dee2aaSAndroid Build Coastguard Worker             return true;
61*c8dee2aaSAndroid Build Coastguard Worker         }
62*c8dee2aaSAndroid Build Coastguard Worker 
63*c8dee2aaSAndroid Build Coastguard Worker         // Everything else is an error.
64*c8dee2aaSAndroid Build Coastguard Worker         reportError();
65*c8dee2aaSAndroid Build Coastguard Worker         return false;
66*c8dee2aaSAndroid Build Coastguard Worker     }
67*c8dee2aaSAndroid Build Coastguard Worker 
68*c8dee2aaSAndroid Build Coastguard Worker     Position errorPosition = {};
69*c8dee2aaSAndroid Build Coastguard Worker     if (!t->isAllowedInUniform(&errorPosition)) {
70*c8dee2aaSAndroid Build Coastguard Worker         reportError();
71*c8dee2aaSAndroid Build Coastguard Worker         if (errorPosition.valid()) {
72*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(errorPosition, "caused by:");
73*c8dee2aaSAndroid Build Coastguard Worker         }
74*c8dee2aaSAndroid Build Coastguard Worker         return false;
75*c8dee2aaSAndroid Build Coastguard Worker     }
76*c8dee2aaSAndroid Build Coastguard Worker 
77*c8dee2aaSAndroid Build Coastguard Worker     return true;
78*c8dee2aaSAndroid Build Coastguard Worker }
79*c8dee2aaSAndroid Build Coastguard Worker 
80*c8dee2aaSAndroid Build Coastguard Worker }  // namespace
81*c8dee2aaSAndroid Build Coastguard Worker 
description() const82*c8dee2aaSAndroid Build Coastguard Worker std::string VarDeclaration::description() const {
83*c8dee2aaSAndroid Build Coastguard Worker     std::string result = this->var()->layout().paddedDescription() +
84*c8dee2aaSAndroid Build Coastguard Worker                          this->var()->modifierFlags().paddedDescription() +
85*c8dee2aaSAndroid Build Coastguard Worker                          this->baseType().description() + ' ' + std::string(this->var()->name());
86*c8dee2aaSAndroid Build Coastguard Worker     if (this->arraySize() > 0) {
87*c8dee2aaSAndroid Build Coastguard Worker         String::appendf(&result, "[%d]", this->arraySize());
88*c8dee2aaSAndroid Build Coastguard Worker     }
89*c8dee2aaSAndroid Build Coastguard Worker     if (this->value()) {
90*c8dee2aaSAndroid Build Coastguard Worker         result += " = " + this->value()->description();
91*c8dee2aaSAndroid Build Coastguard Worker     }
92*c8dee2aaSAndroid Build Coastguard Worker     result += ";";
93*c8dee2aaSAndroid Build Coastguard Worker     return result;
94*c8dee2aaSAndroid Build Coastguard Worker }
95*c8dee2aaSAndroid Build Coastguard Worker 
ErrorCheck(const Context & context,Position pos,Position modifiersPosition,const Layout & layout,ModifierFlags modifierFlags,const Type * type,const Type * baseType,Variable::Storage storage)96*c8dee2aaSAndroid Build Coastguard Worker void VarDeclaration::ErrorCheck(const Context& context,
97*c8dee2aaSAndroid Build Coastguard Worker                                 Position pos,
98*c8dee2aaSAndroid Build Coastguard Worker                                 Position modifiersPosition,
99*c8dee2aaSAndroid Build Coastguard Worker                                 const Layout& layout,
100*c8dee2aaSAndroid Build Coastguard Worker                                 ModifierFlags modifierFlags,
101*c8dee2aaSAndroid Build Coastguard Worker                                 const Type* type,
102*c8dee2aaSAndroid Build Coastguard Worker                                 const Type* baseType,
103*c8dee2aaSAndroid Build Coastguard Worker                                 Variable::Storage storage) {
104*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(type->isArray() ? baseType->matches(type->componentType())
105*c8dee2aaSAndroid Build Coastguard Worker                              : baseType->matches(*type));
106*c8dee2aaSAndroid Build Coastguard Worker 
107*c8dee2aaSAndroid Build Coastguard Worker     if (baseType->componentType().isOpaque() && !baseType->componentType().isAtomic() &&
108*c8dee2aaSAndroid Build Coastguard Worker         storage != Variable::Storage::kGlobal) {
109*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "variables of type '" + baseType->displayName() +
110*c8dee2aaSAndroid Build Coastguard Worker                                     "' must be global");
111*c8dee2aaSAndroid Build Coastguard Worker     }
112*c8dee2aaSAndroid Build Coastguard Worker     if ((modifierFlags & ModifierFlag::kIn) && baseType->isMatrix()) {
113*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "'in' variables may not have matrix type");
114*c8dee2aaSAndroid Build Coastguard Worker     }
115*c8dee2aaSAndroid Build Coastguard Worker     if ((modifierFlags & ModifierFlag::kIn) && type->isUnsizedArray()) {
116*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "'in' variables may not have unsized array type");
117*c8dee2aaSAndroid Build Coastguard Worker     }
118*c8dee2aaSAndroid Build Coastguard Worker     if ((modifierFlags & ModifierFlag::kOut) && type->isUnsizedArray()) {
119*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "'out' variables may not have unsized array type");
120*c8dee2aaSAndroid Build Coastguard Worker     }
121*c8dee2aaSAndroid Build Coastguard Worker     if ((modifierFlags & ModifierFlag::kIn) && modifierFlags.isUniform()) {
122*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "'in uniform' variables not permitted");
123*c8dee2aaSAndroid Build Coastguard Worker     }
124*c8dee2aaSAndroid Build Coastguard Worker     if (modifierFlags.isReadOnly() && modifierFlags.isWriteOnly()) {
125*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "'readonly' and 'writeonly' qualifiers cannot be combined");
126*c8dee2aaSAndroid Build Coastguard Worker     }
127*c8dee2aaSAndroid Build Coastguard Worker     if (modifierFlags.isUniform() && modifierFlags.isBuffer()) {
128*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "'uniform buffer' variables not permitted");
129*c8dee2aaSAndroid Build Coastguard Worker     }
130*c8dee2aaSAndroid Build Coastguard Worker     if (modifierFlags.isWorkgroup() && (modifierFlags & (ModifierFlag::kIn |
131*c8dee2aaSAndroid Build Coastguard Worker                                                          ModifierFlag::kOut))) {
132*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "in / out variables may not be declared workgroup");
133*c8dee2aaSAndroid Build Coastguard Worker     }
134*c8dee2aaSAndroid Build Coastguard Worker     if (modifierFlags.isUniform()) {
135*c8dee2aaSAndroid Build Coastguard Worker         check_valid_uniform_type(pos, baseType, context);
136*c8dee2aaSAndroid Build Coastguard Worker     }
137*c8dee2aaSAndroid Build Coastguard Worker     if (baseType->isEffectChild() && !modifierFlags.isUniform()) {
138*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "variables of type '" + baseType->displayName() +
139*c8dee2aaSAndroid Build Coastguard Worker                                     "' must be uniform");
140*c8dee2aaSAndroid Build Coastguard Worker     }
141*c8dee2aaSAndroid Build Coastguard Worker     if (baseType->isEffectChild() && context.fConfig->fKind == ProgramKind::kMeshVertex) {
142*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "effects are not permitted in mesh vertex shaders");
143*c8dee2aaSAndroid Build Coastguard Worker     }
144*c8dee2aaSAndroid Build Coastguard Worker     if (baseType->isOrContainsAtomic()) {
145*c8dee2aaSAndroid Build Coastguard Worker         // An atomic variable (or a struct or an array that contains an atomic member) must be
146*c8dee2aaSAndroid Build Coastguard Worker         // either:
147*c8dee2aaSAndroid Build Coastguard Worker         //   a. Declared as a workgroup-shared variable, OR
148*c8dee2aaSAndroid Build Coastguard Worker         //   b. Declared as the member of writable storage buffer block (i.e. has no readonly
149*c8dee2aaSAndroid Build Coastguard Worker         //   restriction).
150*c8dee2aaSAndroid Build Coastguard Worker         //
151*c8dee2aaSAndroid Build Coastguard Worker         // The checks below will enforce these two rules on all declarations. If the variable is not
152*c8dee2aaSAndroid Build Coastguard Worker         // declared with the workgroup modifier, then it must be declared in the interface block
153*c8dee2aaSAndroid Build Coastguard Worker         // storage. If this is the declaration for an interface block that contains an atomic
154*c8dee2aaSAndroid Build Coastguard Worker         // member, then it must have the `buffer` modifier and no `readonly` modifier.
155*c8dee2aaSAndroid Build Coastguard Worker         bool isBlockMember = (storage == Variable::Storage::kInterfaceBlock);
156*c8dee2aaSAndroid Build Coastguard Worker         bool isWritableStorageBuffer = modifierFlags.isBuffer() && !modifierFlags.isReadOnly();
157*c8dee2aaSAndroid Build Coastguard Worker 
158*c8dee2aaSAndroid Build Coastguard Worker         if (!modifierFlags.isWorkgroup() &&
159*c8dee2aaSAndroid Build Coastguard Worker             !(baseType->isInterfaceBlock() ? isWritableStorageBuffer : isBlockMember)) {
160*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(pos, "atomics are only permitted in workgroup variables and "
161*c8dee2aaSAndroid Build Coastguard Worker                                         "writable storage blocks");
162*c8dee2aaSAndroid Build Coastguard Worker         }
163*c8dee2aaSAndroid Build Coastguard Worker     }
164*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fFlags & LayoutFlag::kColor) {
165*c8dee2aaSAndroid Build Coastguard Worker         if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
166*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(pos, "'layout(color)' is only permitted in runtime effects");
167*c8dee2aaSAndroid Build Coastguard Worker         }
168*c8dee2aaSAndroid Build Coastguard Worker         if (!modifierFlags.isUniform()) {
169*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(pos, "'layout(color)' is only permitted on 'uniform' variables");
170*c8dee2aaSAndroid Build Coastguard Worker         }
171*c8dee2aaSAndroid Build Coastguard Worker         auto validColorXformType = [](const Type& t) {
172*c8dee2aaSAndroid Build Coastguard Worker             return t.isVector() && t.componentType().isFloat() &&
173*c8dee2aaSAndroid Build Coastguard Worker                    (t.columns() == 3 || t.columns() == 4);
174*c8dee2aaSAndroid Build Coastguard Worker         };
175*c8dee2aaSAndroid Build Coastguard Worker         if (!validColorXformType(*baseType)) {
176*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(pos, "'layout(color)' is not permitted on variables of type '" +
177*c8dee2aaSAndroid Build Coastguard Worker                                         baseType->displayName() + "'");
178*c8dee2aaSAndroid Build Coastguard Worker         }
179*c8dee2aaSAndroid Build Coastguard Worker     }
180*c8dee2aaSAndroid Build Coastguard Worker 
181*c8dee2aaSAndroid Build Coastguard Worker     ModifierFlags permitted = ModifierFlag::kConst | ModifierFlag::kHighp | ModifierFlag::kMediump |
182*c8dee2aaSAndroid Build Coastguard Worker                               ModifierFlag::kLowp;
183*c8dee2aaSAndroid Build Coastguard Worker     if (storage == Variable::Storage::kGlobal) {
184*c8dee2aaSAndroid Build Coastguard Worker         // Uniforms are allowed in all programs
185*c8dee2aaSAndroid Build Coastguard Worker         permitted |= ModifierFlag::kUniform;
186*c8dee2aaSAndroid Build Coastguard Worker 
187*c8dee2aaSAndroid Build Coastguard Worker         // No other modifiers are allowed in runtime effects.
188*c8dee2aaSAndroid Build Coastguard Worker         if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
189*c8dee2aaSAndroid Build Coastguard Worker             if (baseType->isInterfaceBlock()) {
190*c8dee2aaSAndroid Build Coastguard Worker                 // Interface blocks allow `buffer`.
191*c8dee2aaSAndroid Build Coastguard Worker                 permitted |= ModifierFlag::kBuffer;
192*c8dee2aaSAndroid Build Coastguard Worker 
193*c8dee2aaSAndroid Build Coastguard Worker                 if (modifierFlags.isBuffer()) {
194*c8dee2aaSAndroid Build Coastguard Worker                     // Only storage blocks allow `readonly` and `writeonly`.
195*c8dee2aaSAndroid Build Coastguard Worker                     // (`readonly` and `writeonly` textures are converted to separate types via
196*c8dee2aaSAndroid Build Coastguard Worker                     // applyAccessQualifiers.)
197*c8dee2aaSAndroid Build Coastguard Worker                     permitted |= ModifierFlag::kReadOnly | ModifierFlag::kWriteOnly;
198*c8dee2aaSAndroid Build Coastguard Worker                 }
199*c8dee2aaSAndroid Build Coastguard Worker 
200*c8dee2aaSAndroid Build Coastguard Worker                 // It is an error for an unsized array to appear anywhere but the last member of a
201*c8dee2aaSAndroid Build Coastguard Worker                 // "buffer" block.
202*c8dee2aaSAndroid Build Coastguard Worker                 const auto& fields = baseType->fields();
203*c8dee2aaSAndroid Build Coastguard Worker                 const int illegalRangeEnd = SkToInt(fields.size()) -
204*c8dee2aaSAndroid Build Coastguard Worker                                             (modifierFlags.isBuffer() ? 1 : 0);
205*c8dee2aaSAndroid Build Coastguard Worker                 for (int i = 0; i < illegalRangeEnd; ++i) {
206*c8dee2aaSAndroid Build Coastguard Worker                     if (fields[i].fType->isUnsizedArray()) {
207*c8dee2aaSAndroid Build Coastguard Worker                         context.fErrors->error(
208*c8dee2aaSAndroid Build Coastguard Worker                                 fields[i].fPosition,
209*c8dee2aaSAndroid Build Coastguard Worker                                 "unsized array must be the last member of a storage block");
210*c8dee2aaSAndroid Build Coastguard Worker                     }
211*c8dee2aaSAndroid Build Coastguard Worker                 }
212*c8dee2aaSAndroid Build Coastguard Worker             }
213*c8dee2aaSAndroid Build Coastguard Worker 
214*c8dee2aaSAndroid Build Coastguard Worker             if (!baseType->isOpaque()) {
215*c8dee2aaSAndroid Build Coastguard Worker                 // Only non-opaque types allow `in` and `out`.
216*c8dee2aaSAndroid Build Coastguard Worker                 permitted |= ModifierFlag::kIn | ModifierFlag::kOut;
217*c8dee2aaSAndroid Build Coastguard Worker             }
218*c8dee2aaSAndroid Build Coastguard Worker             if (ProgramConfig::IsFragment(context.fConfig->fKind) && baseType->isStruct() &&
219*c8dee2aaSAndroid Build Coastguard Worker                 !baseType->isInterfaceBlock()) {
220*c8dee2aaSAndroid Build Coastguard Worker                 // Only structs in fragment shaders allow `pixel_local`.
221*c8dee2aaSAndroid Build Coastguard Worker                 permitted |= ModifierFlag::kPixelLocal;
222*c8dee2aaSAndroid Build Coastguard Worker             }
223*c8dee2aaSAndroid Build Coastguard Worker             if (ProgramConfig::IsCompute(context.fConfig->fKind)) {
224*c8dee2aaSAndroid Build Coastguard Worker                 // Only compute shaders allow `workgroup`.
225*c8dee2aaSAndroid Build Coastguard Worker                 if (!baseType->isOpaque() || baseType->isAtomic()) {
226*c8dee2aaSAndroid Build Coastguard Worker                     permitted |= ModifierFlag::kWorkgroup;
227*c8dee2aaSAndroid Build Coastguard Worker                 }
228*c8dee2aaSAndroid Build Coastguard Worker             } else {
229*c8dee2aaSAndroid Build Coastguard Worker                 // Only vertex/fragment shaders allow `flat` and `noperspective`.
230*c8dee2aaSAndroid Build Coastguard Worker                 permitted |= ModifierFlag::kFlat | ModifierFlag::kNoPerspective;
231*c8dee2aaSAndroid Build Coastguard Worker             }
232*c8dee2aaSAndroid Build Coastguard Worker         }
233*c8dee2aaSAndroid Build Coastguard Worker     }
234*c8dee2aaSAndroid Build Coastguard Worker 
235*c8dee2aaSAndroid Build Coastguard Worker     LayoutFlags permittedLayoutFlags = LayoutFlag::kAll;
236*c8dee2aaSAndroid Build Coastguard Worker 
237*c8dee2aaSAndroid Build Coastguard Worker     // Pixel format modifiers are required on storage textures, and forbidden on other types.
238*c8dee2aaSAndroid Build Coastguard Worker     if (baseType->isStorageTexture()) {
239*c8dee2aaSAndroid Build Coastguard Worker         if (!(layout.fFlags & LayoutFlag::kAllPixelFormats)) {
240*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(pos, "storage textures must declare a pixel format");
241*c8dee2aaSAndroid Build Coastguard Worker         }
242*c8dee2aaSAndroid Build Coastguard Worker     } else {
243*c8dee2aaSAndroid Build Coastguard Worker         permittedLayoutFlags &= ~LayoutFlag::kAllPixelFormats;
244*c8dee2aaSAndroid Build Coastguard Worker     }
245*c8dee2aaSAndroid Build Coastguard Worker 
246*c8dee2aaSAndroid Build Coastguard Worker     // The `texture` and `sampler` modifiers can be present respectively on a texture and sampler or
247*c8dee2aaSAndroid Build Coastguard Worker     // simultaneously on a combined image-sampler but they are not permitted on any other type.
248*c8dee2aaSAndroid Build Coastguard Worker     switch (baseType->typeKind()) {
249*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kSampler:
250*c8dee2aaSAndroid Build Coastguard Worker             // Both texture and sampler flags are permitted
251*c8dee2aaSAndroid Build Coastguard Worker             break;
252*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kTexture:
253*c8dee2aaSAndroid Build Coastguard Worker             permittedLayoutFlags &= ~LayoutFlag::kSampler;
254*c8dee2aaSAndroid Build Coastguard Worker             break;
255*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kSeparateSampler:
256*c8dee2aaSAndroid Build Coastguard Worker             permittedLayoutFlags &= ~LayoutFlag::kTexture;
257*c8dee2aaSAndroid Build Coastguard Worker             break;
258*c8dee2aaSAndroid Build Coastguard Worker         default:
259*c8dee2aaSAndroid Build Coastguard Worker             permittedLayoutFlags &= ~(LayoutFlag::kTexture | LayoutFlag::kSampler);
260*c8dee2aaSAndroid Build Coastguard Worker             break;
261*c8dee2aaSAndroid Build Coastguard Worker     }
262*c8dee2aaSAndroid Build Coastguard Worker 
263*c8dee2aaSAndroid Build Coastguard Worker     // We don't allow 'binding' or 'set' on normal uniform variables, only on textures, samplers,
264*c8dee2aaSAndroid Build Coastguard Worker     // and interface blocks (holding uniform variables). They're also only allowed at global scope,
265*c8dee2aaSAndroid Build Coastguard Worker     // not on interface block fields (or locals/parameters).
266*c8dee2aaSAndroid Build Coastguard Worker     bool permitBindingAndSet = baseType->typeKind() == Type::TypeKind::kSampler ||
267*c8dee2aaSAndroid Build Coastguard Worker                                baseType->typeKind() == Type::TypeKind::kSeparateSampler ||
268*c8dee2aaSAndroid Build Coastguard Worker                                baseType->typeKind() == Type::TypeKind::kTexture ||
269*c8dee2aaSAndroid Build Coastguard Worker                                baseType->isInterfaceBlock();
270*c8dee2aaSAndroid Build Coastguard Worker     if (storage != Variable::Storage::kGlobal || (modifierFlags.isUniform() &&
271*c8dee2aaSAndroid Build Coastguard Worker                                                   !permitBindingAndSet)) {
272*c8dee2aaSAndroid Build Coastguard Worker         permittedLayoutFlags &= ~LayoutFlag::kBinding;
273*c8dee2aaSAndroid Build Coastguard Worker         permittedLayoutFlags &= ~LayoutFlag::kSet;
274*c8dee2aaSAndroid Build Coastguard Worker         permittedLayoutFlags &= ~LayoutFlag::kAllBackends;
275*c8dee2aaSAndroid Build Coastguard Worker     }
276*c8dee2aaSAndroid Build Coastguard Worker     if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
277*c8dee2aaSAndroid Build Coastguard Worker         // Disallow all layout flags except 'color' in runtime effects
278*c8dee2aaSAndroid Build Coastguard Worker         permittedLayoutFlags &= LayoutFlag::kColor;
279*c8dee2aaSAndroid Build Coastguard Worker     }
280*c8dee2aaSAndroid Build Coastguard Worker 
281*c8dee2aaSAndroid Build Coastguard Worker     // The `push_constant` flag isn't allowed on in-variables, out-variables, bindings or sets.
282*c8dee2aaSAndroid Build Coastguard Worker     if ((layout.fFlags & (LayoutFlag::kSet | LayoutFlag::kBinding)) ||
283*c8dee2aaSAndroid Build Coastguard Worker         (modifierFlags & (ModifierFlag::kIn | ModifierFlag::kOut))) {
284*c8dee2aaSAndroid Build Coastguard Worker         permittedLayoutFlags &= ~LayoutFlag::kPushConstant;
285*c8dee2aaSAndroid Build Coastguard Worker     }
286*c8dee2aaSAndroid Build Coastguard Worker     // The `builtin` layout flag is only allowed in modules.
287*c8dee2aaSAndroid Build Coastguard Worker     if (!context.fConfig->isBuiltinCode()) {
288*c8dee2aaSAndroid Build Coastguard Worker         permittedLayoutFlags &= ~LayoutFlag::kBuiltin;
289*c8dee2aaSAndroid Build Coastguard Worker     }
290*c8dee2aaSAndroid Build Coastguard Worker 
291*c8dee2aaSAndroid Build Coastguard Worker     modifierFlags.checkPermittedFlags(context, modifiersPosition, permitted);
292*c8dee2aaSAndroid Build Coastguard Worker     layout.checkPermittedLayout(context, modifiersPosition, permittedLayoutFlags);
293*c8dee2aaSAndroid Build Coastguard Worker }
294*c8dee2aaSAndroid Build Coastguard Worker 
ErrorCheckAndCoerce(const Context & context,const Variable & var,const Type * baseType,std::unique_ptr<Expression> & value)295*c8dee2aaSAndroid Build Coastguard Worker bool VarDeclaration::ErrorCheckAndCoerce(const Context& context,
296*c8dee2aaSAndroid Build Coastguard Worker                                          const Variable& var,
297*c8dee2aaSAndroid Build Coastguard Worker                                          const Type* baseType,
298*c8dee2aaSAndroid Build Coastguard Worker                                          std::unique_ptr<Expression>& value) {
299*c8dee2aaSAndroid Build Coastguard Worker     if (baseType->matches(*context.fTypes.fInvalid)) {
300*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(var.fPosition, "invalid type");
301*c8dee2aaSAndroid Build Coastguard Worker         return false;
302*c8dee2aaSAndroid Build Coastguard Worker     }
303*c8dee2aaSAndroid Build Coastguard Worker     if (baseType->isVoid()) {
304*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(var.fPosition, "variables of type 'void' are not allowed");
305*c8dee2aaSAndroid Build Coastguard Worker         return false;
306*c8dee2aaSAndroid Build Coastguard Worker     }
307*c8dee2aaSAndroid Build Coastguard Worker 
308*c8dee2aaSAndroid Build Coastguard Worker     ErrorCheck(context, var.fPosition, var.modifiersPosition(), var.layout(), var.modifierFlags(),
309*c8dee2aaSAndroid Build Coastguard Worker                &var.type(), baseType, var.storage());
310*c8dee2aaSAndroid Build Coastguard Worker     if (value) {
311*c8dee2aaSAndroid Build Coastguard Worker         if (var.type().isOpaque() || var.type().isOrContainsAtomic()) {
312*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(value->fPosition, "opaque type '" + var.type().displayName() +
313*c8dee2aaSAndroid Build Coastguard Worker                                                      "' cannot use initializer expressions");
314*c8dee2aaSAndroid Build Coastguard Worker             return false;
315*c8dee2aaSAndroid Build Coastguard Worker         }
316*c8dee2aaSAndroid Build Coastguard Worker         if (var.modifierFlags() & ModifierFlag::kIn) {
317*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(value->fPosition,
318*c8dee2aaSAndroid Build Coastguard Worker                                    "'in' variables cannot use initializer expressions");
319*c8dee2aaSAndroid Build Coastguard Worker             return false;
320*c8dee2aaSAndroid Build Coastguard Worker         }
321*c8dee2aaSAndroid Build Coastguard Worker         if (var.modifierFlags().isUniform()) {
322*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(value->fPosition,
323*c8dee2aaSAndroid Build Coastguard Worker                                    "'uniform' variables cannot use initializer expressions");
324*c8dee2aaSAndroid Build Coastguard Worker             return false;
325*c8dee2aaSAndroid Build Coastguard Worker         }
326*c8dee2aaSAndroid Build Coastguard Worker         if (var.storage() == Variable::Storage::kInterfaceBlock) {
327*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(value->fPosition,
328*c8dee2aaSAndroid Build Coastguard Worker                                    "initializers are not permitted on interface block fields");
329*c8dee2aaSAndroid Build Coastguard Worker             return false;
330*c8dee2aaSAndroid Build Coastguard Worker         }
331*c8dee2aaSAndroid Build Coastguard Worker         if (context.fConfig->strictES2Mode() && var.type().isOrContainsArray()) {
332*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(value->fPosition, "initializers are not permitted on arrays "
333*c8dee2aaSAndroid Build Coastguard Worker                                                      "(or structs containing arrays)");
334*c8dee2aaSAndroid Build Coastguard Worker             return false;
335*c8dee2aaSAndroid Build Coastguard Worker         }
336*c8dee2aaSAndroid Build Coastguard Worker         value = var.type().coerceExpression(std::move(value), context);
337*c8dee2aaSAndroid Build Coastguard Worker         if (!value) {
338*c8dee2aaSAndroid Build Coastguard Worker             return false;
339*c8dee2aaSAndroid Build Coastguard Worker         }
340*c8dee2aaSAndroid Build Coastguard Worker     }
341*c8dee2aaSAndroid Build Coastguard Worker     if (var.modifierFlags().isConst()) {
342*c8dee2aaSAndroid Build Coastguard Worker         if (!value) {
343*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(var.fPosition, "'const' variables must be initialized");
344*c8dee2aaSAndroid Build Coastguard Worker             return false;
345*c8dee2aaSAndroid Build Coastguard Worker         }
346*c8dee2aaSAndroid Build Coastguard Worker         if (!Analysis::IsConstantExpression(*value)) {
347*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(value->fPosition,
348*c8dee2aaSAndroid Build Coastguard Worker                                    "'const' variable initializer must be a constant expression");
349*c8dee2aaSAndroid Build Coastguard Worker             return false;
350*c8dee2aaSAndroid Build Coastguard Worker         }
351*c8dee2aaSAndroid Build Coastguard Worker     }
352*c8dee2aaSAndroid Build Coastguard Worker     if (var.storage() == Variable::Storage::kInterfaceBlock) {
353*c8dee2aaSAndroid Build Coastguard Worker         if (var.type().isOpaque()) {
354*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(var.fPosition, "opaque type '" + var.type().displayName() +
355*c8dee2aaSAndroid Build Coastguard Worker                                                   "' is not permitted in an interface block");
356*c8dee2aaSAndroid Build Coastguard Worker             return false;
357*c8dee2aaSAndroid Build Coastguard Worker         }
358*c8dee2aaSAndroid Build Coastguard Worker     }
359*c8dee2aaSAndroid Build Coastguard Worker     if (var.storage() == Variable::Storage::kGlobal) {
360*c8dee2aaSAndroid Build Coastguard Worker         if (value && !Analysis::IsConstantExpression(*value)) {
361*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(value->fPosition,
362*c8dee2aaSAndroid Build Coastguard Worker                                    "global variable initializer must be a constant expression");
363*c8dee2aaSAndroid Build Coastguard Worker             return false;
364*c8dee2aaSAndroid Build Coastguard Worker         }
365*c8dee2aaSAndroid Build Coastguard Worker     }
366*c8dee2aaSAndroid Build Coastguard Worker     return true;
367*c8dee2aaSAndroid Build Coastguard Worker }
368*c8dee2aaSAndroid Build Coastguard Worker 
Convert(const Context & context,Position overallPos,const Modifiers & modifiers,const Type & type,Position namePos,std::string_view name,VariableStorage storage,std::unique_ptr<Expression> value)369*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<VarDeclaration> VarDeclaration::Convert(const Context& context,
370*c8dee2aaSAndroid Build Coastguard Worker                                                         Position overallPos,
371*c8dee2aaSAndroid Build Coastguard Worker                                                         const Modifiers& modifiers,
372*c8dee2aaSAndroid Build Coastguard Worker                                                         const Type& type,
373*c8dee2aaSAndroid Build Coastguard Worker                                                         Position namePos,
374*c8dee2aaSAndroid Build Coastguard Worker                                                         std::string_view name,
375*c8dee2aaSAndroid Build Coastguard Worker                                                         VariableStorage storage,
376*c8dee2aaSAndroid Build Coastguard Worker                                                         std::unique_ptr<Expression> value) {
377*c8dee2aaSAndroid Build Coastguard Worker     // Parameter declaration-statements do not exist in the grammar (unlike, say, K&R C).
378*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(storage != VariableStorage::kParameter);
379*c8dee2aaSAndroid Build Coastguard Worker 
380*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Variable> var = Variable::Convert(context,
381*c8dee2aaSAndroid Build Coastguard Worker                                                       overallPos,
382*c8dee2aaSAndroid Build Coastguard Worker                                                       modifiers.fPosition,
383*c8dee2aaSAndroid Build Coastguard Worker                                                       modifiers.fLayout,
384*c8dee2aaSAndroid Build Coastguard Worker                                                       modifiers.fFlags,
385*c8dee2aaSAndroid Build Coastguard Worker                                                       &type,
386*c8dee2aaSAndroid Build Coastguard Worker                                                       namePos,
387*c8dee2aaSAndroid Build Coastguard Worker                                                       name,
388*c8dee2aaSAndroid Build Coastguard Worker                                                       storage);
389*c8dee2aaSAndroid Build Coastguard Worker     if (!var) {
390*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
391*c8dee2aaSAndroid Build Coastguard Worker     }
392*c8dee2aaSAndroid Build Coastguard Worker     return VarDeclaration::Convert(context, std::move(var), std::move(value));
393*c8dee2aaSAndroid Build Coastguard Worker }
394*c8dee2aaSAndroid Build Coastguard Worker 
Convert(const Context & context,std::unique_ptr<Variable> var,std::unique_ptr<Expression> value)395*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<VarDeclaration> VarDeclaration::Convert(const Context& context,
396*c8dee2aaSAndroid Build Coastguard Worker                                                         std::unique_ptr<Variable> var,
397*c8dee2aaSAndroid Build Coastguard Worker                                                         std::unique_ptr<Expression> value) {
398*c8dee2aaSAndroid Build Coastguard Worker     const Type* baseType = &var->type();
399*c8dee2aaSAndroid Build Coastguard Worker     int arraySize = 0;
400*c8dee2aaSAndroid Build Coastguard Worker     if (baseType->isArray()) {
401*c8dee2aaSAndroid Build Coastguard Worker         arraySize = baseType->columns();
402*c8dee2aaSAndroid Build Coastguard Worker         baseType = &baseType->componentType();
403*c8dee2aaSAndroid Build Coastguard Worker     }
404*c8dee2aaSAndroid Build Coastguard Worker     if (!ErrorCheckAndCoerce(context, *var, baseType, value)) {
405*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
406*c8dee2aaSAndroid Build Coastguard Worker     }
407*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<VarDeclaration> varDecl = VarDeclaration::Make(context, var.get(), baseType,
408*c8dee2aaSAndroid Build Coastguard Worker                                                                    arraySize, std::move(value));
409*c8dee2aaSAndroid Build Coastguard Worker     if (!varDecl) {
410*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
411*c8dee2aaSAndroid Build Coastguard Worker     }
412*c8dee2aaSAndroid Build Coastguard Worker 
413*c8dee2aaSAndroid Build Coastguard Worker     if (var->storage() == Variable::Storage::kGlobal ||
414*c8dee2aaSAndroid Build Coastguard Worker         var->storage() == Variable::Storage::kInterfaceBlock) {
415*c8dee2aaSAndroid Build Coastguard Worker         // Check if this globally-scoped variable name overlaps an existing symbol name.
416*c8dee2aaSAndroid Build Coastguard Worker         if (context.fSymbolTable->find(var->name())) {
417*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(var->fPosition,
418*c8dee2aaSAndroid Build Coastguard Worker                                    "symbol '" + std::string(var->name()) + "' was already defined");
419*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
420*c8dee2aaSAndroid Build Coastguard Worker         }
421*c8dee2aaSAndroid Build Coastguard Worker 
422*c8dee2aaSAndroid Build Coastguard Worker         // `sk_RTAdjust` is special, and makes the IR generator emit position-fixup expressions.
423*c8dee2aaSAndroid Build Coastguard Worker         if (var->name() == Compiler::RTADJUST_NAME) {
424*c8dee2aaSAndroid Build Coastguard Worker             if (!var->type().matches(*context.fTypes.fFloat4)) {
425*c8dee2aaSAndroid Build Coastguard Worker                 context.fErrors->error(var->fPosition, "sk_RTAdjust must have type 'float4'");
426*c8dee2aaSAndroid Build Coastguard Worker                 return nullptr;
427*c8dee2aaSAndroid Build Coastguard Worker             }
428*c8dee2aaSAndroid Build Coastguard Worker         }
429*c8dee2aaSAndroid Build Coastguard Worker     }
430*c8dee2aaSAndroid Build Coastguard Worker 
431*c8dee2aaSAndroid Build Coastguard Worker     context.fSymbolTable->add(context, std::move(var));
432*c8dee2aaSAndroid Build Coastguard Worker     return varDecl;
433*c8dee2aaSAndroid Build Coastguard Worker }
434*c8dee2aaSAndroid Build Coastguard Worker 
Make(const Context & context,Variable * var,const Type * baseType,int arraySize,std::unique_ptr<Expression> value)435*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<VarDeclaration> VarDeclaration::Make(const Context& context,
436*c8dee2aaSAndroid Build Coastguard Worker                                                      Variable* var,
437*c8dee2aaSAndroid Build Coastguard Worker                                                      const Type* baseType,
438*c8dee2aaSAndroid Build Coastguard Worker                                                      int arraySize,
439*c8dee2aaSAndroid Build Coastguard Worker                                                      std::unique_ptr<Expression> value) {
440*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!baseType->isArray());
441*c8dee2aaSAndroid Build Coastguard Worker     // function parameters cannot have variable declarations
442*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(var->storage() != Variable::Storage::kParameter);
443*c8dee2aaSAndroid Build Coastguard Worker     // 'const' variables must be initialized
444*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!var->modifierFlags().isConst() || value);
445*c8dee2aaSAndroid Build Coastguard Worker     // 'const' variable initializer must be a constant expression
446*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!var->modifierFlags().isConst() || Analysis::IsConstantExpression(*value));
447*c8dee2aaSAndroid Build Coastguard Worker     // global variable initializer must be a constant expression
448*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!(value && var->storage() == Variable::Storage::kGlobal &&
449*c8dee2aaSAndroid Build Coastguard Worker                !Analysis::IsConstantExpression(*value)));
450*c8dee2aaSAndroid Build Coastguard Worker     // opaque type not permitted on an interface block
451*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && var->type().isOpaque()));
452*c8dee2aaSAndroid Build Coastguard Worker     // initializers are not permitted on interface block fields
453*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && value));
454*c8dee2aaSAndroid Build Coastguard Worker     // opaque type cannot use initializer expressions
455*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!(value && var->type().isOpaque()));
456*c8dee2aaSAndroid Build Coastguard Worker     // 'in' variables cannot use initializer expressions
457*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!(value && (var->modifierFlags() & ModifierFlag::kIn)));
458*c8dee2aaSAndroid Build Coastguard Worker     // 'uniform' variables cannot use initializer expressions
459*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!(value && var->modifierFlags().isUniform()));
460*c8dee2aaSAndroid Build Coastguard Worker     // in strict-ES2 mode, is-or-contains-array types cannot use initializer expressions
461*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!(value && var->type().isOrContainsArray() && context.fConfig->strictES2Mode()));
462*c8dee2aaSAndroid Build Coastguard Worker 
463*c8dee2aaSAndroid Build Coastguard Worker     auto result = std::make_unique<VarDeclaration>(var, baseType, arraySize, std::move(value));
464*c8dee2aaSAndroid Build Coastguard Worker     var->setVarDeclaration(result.get());
465*c8dee2aaSAndroid Build Coastguard Worker     return result;
466*c8dee2aaSAndroid Build Coastguard Worker }
467*c8dee2aaSAndroid Build Coastguard Worker 
468*c8dee2aaSAndroid Build Coastguard Worker }  // namespace SkSL
469