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