82#include <initializer_list>
90#ifdef SK_ENABLE_WGSL_VALIDATION
92#include "src/tint/lang/wgsl/reader/options.h"
93#include "src/tint/lang/wgsl/extension.h"
105enum class WGSLFunctionDependency : uint8_t {
107 kPipelineInputs = 1 << 0,
108 kPipelineOutputs = 1 << 1,
156 WGSLFunctionDependencies>;
170 :
INHERITED(context, caps, program, out) {}
179 void preprocessProgram();
182 void write(std::string_view
s);
183 void writeLine(std::string_view
s = std::string_view());
187 void writePipelineIODeclaration(
const Layout& layout,
189 std::string_view
name,
191 void writeUserDefinedIODecl(
const Layout& layout,
193 std::string_view
name,
195 void writeBuiltinIODecl(
const Type&
type,
196 std::string_view
name,
199 void writeVariableDecl(
const Layout& layout,
201 std::string_view
name,
215 void writeBlock(
const Block&
b);
217 void writeExpressionStatement(
const Expression& expr);
224 std::string_view switchValue);
232 class VectorComponentLValue;
233 std::unique_ptr<LValue> makeLValue(
const Expression& e);
236 std::string variablePrefix(
const Variable& v);
243 std::string assembleExpression(
const Expression& e, Precedence parentPrecedence);
244 std::string assembleBinaryExpression(
const BinaryExpression&
b, Precedence parentPrecedence);
248 const Type& resultType,
249 Precedence parentPrecedence);
250 std::string assembleFieldAccess(
const FieldAccess& f);
251 std::string assembleFunctionCall(
const FunctionCall& call, Precedence parentPrecedence);
253 std::string assembleLiteral(
const Literal& l);
254 std::string assemblePostfixExpression(
const PostfixExpression& p, Precedence parentPrecedence);
255 std::string assemblePrefixExpression(
const PrefixExpression& p, Precedence parentPrecedence);
256 std::string assembleSwizzle(
const Swizzle& swizzle);
257 std::string assembleTernaryExpression(
const TernaryExpression& t, Precedence parentPrecedence);
259 std::string assembleName(std::string_view
name);
261 std::string assembleIncrementExpr(
const Type&
type);
264 std::string assembleIntrinsicCall(
const FunctionCall& call,
266 Precedence parentPrecedence);
267 std::string assembleSimpleIntrinsic(std::string_view intrinsicName,
const FunctionCall& call);
268 std::string assembleUnaryOpIntrinsic(
Operator op,
270 Precedence parentPrecedence);
271 std::string assembleBinaryOpIntrinsic(
Operator op,
273 Precedence parentPrecedence);
274 std::string assembleVectorizedIntrinsic(std::string_view intrinsicName,
276 std::string assembleOutAssignedIntrinsic(std::string_view intrinsicName,
277 std::string_view returnFieldName,
278 std::string_view outFieldName,
280 std::string assemblePartialSampleCall(std::string_view intrinsicName,
283 std::string assembleInversePolyfill(
const FunctionCall& call);
284 std::string assembleComponentwiseMatrixBinary(
const Type& leftType,
285 const Type& rightType,
286 const std::string&
left,
287 const std::string&
right,
299 std::string assembleEqualityExpression(
const Type&
left,
300 const std::string& leftName,
302 const std::string& rightName,
304 Precedence parentPrecedence);
308 Precedence parentPrecedence);
311 std::string writeScratchVar(
const Type&
type,
const std::string& value =
"");
315 std::string writeScratchLet(
const std::string& expr);
316 std::string writeScratchLet(
const Expression& expr, Precedence parentPrecedence);
321 std::string writeNontrivialScratchLet(
const Expression& expr, Precedence parentPrecedence);
336 bool needsStageInputStruct()
const;
337 void writeStageInputStruct();
338 bool needsStageOutputStruct()
const;
339 void writeStageOutputStruct();
340 void writeUniformsAndBuffers();
341 void prepareUniformPolyfillsForInterfaceBlock(
const InterfaceBlock* interfaceBlock,
342 std::string_view instanceName,
345 void writeUniformPolyfills();
347 void writeTextureOrSampler(
const Variable& var,
349 std::string_view suffix,
350 std::string_view wgslType);
363 void writeNonBlockUniformsForTests();
379 ProgramRequirements fRequirements;
385 bool fWrittenInverse2 =
false;
386 bool fWrittenInverse3 =
false;
387 bool fWrittenInverse4 =
false;
394 struct FieldPolyfillInfo {
396 std::string fReplacementName;
397 bool fIsArray =
false;
398 bool fIsMatrix =
false;
399 bool fWasAccessed =
false;
402 FieldPolyfillMap fFieldPolyfillMap;
405 int fIndentation = 0;
406 bool fAtLineStart =
false;
407 bool fHasUnconditionalReturn =
false;
408 bool fAtFunctionScope =
false;
409 int fConditionalScopeDepth = 0;
414 int fScratchCount = 0;
421static constexpr char kSamplerSuffix[] =
"_Sampler";
422static constexpr char kTextureSuffix[] =
"_Texture";
425enum class PtrAddressSpace {
433 case Operator::Kind::LOGICALXOR:
return " != ";
434 default:
return op.operatorName();
438bool is_reserved_word(std::string_view word) {
502 "texture_cube_array",
503 "texture_multisampled_2d",
504 "texture_storage_1d",
505 "texture_storage_2d",
506 "texture_storage_2d_array",
507 "texture_storage_3d",
531 "local_invocation_id",
532 "local_invocation_index",
533 "global_invocation_id",
704 return kReservedWords.
contains(word);
707std::string_view pipeline_struct_prefix(
ProgramKind kind) {
721std::string_view address_space_to_str(PtrAddressSpace addressSpace) {
722 switch (addressSpace) {
723 case PtrAddressSpace::kFunction:
725 case PtrAddressSpace::kPrivate:
727 case PtrAddressSpace::kStorage:
731 return "unsupported";
734std::string_view to_scalar_type(
const Type&
type) {
736 switch (
type.numberKind()) {
758std::string to_wgsl_type(
const Context& context,
const Type& raw,
const Layout* layout =
nullptr) {
759 const Type&
type = raw.resolve().scalarTypeForLiteral();
760 switch (
type.typeKind()) {
762 return std::string(to_scalar_type(
type));
766 return "atomic<u32>";
769 std::string_view ct = to_scalar_type(
type.componentType());
773 std::string_view ct = to_scalar_type(
type.componentType());
775 type.columns(),
type.rows(), (
int)ct.length(), ct.data());
778 std::string
result =
"array<" + to_wgsl_type(context,
type.componentType(), layout);
779 if (!
type.isUnsizedArray()) {
786 if (
type.matches(*context.fTypes.fWriteOnlyTexture2D)) {
787 std::string
result =
"texture_storage_2d<";
791 switch (pixelFormat.value()) {
793 return result +
"rgba8unorm, write>";
796 return result +
"rgba32float, write>";
799 return result +
"r32float, write>";
806 if (
type.matches(*context.fTypes.fReadOnlyTexture2D)) {
807 return "texture_2d<f32>";
814 return std::string(
type.name());
817std::string to_ptr_type(
const Context& context,
819 const Layout* layout,
820 PtrAddressSpace addressSpace = PtrAddressSpace::kFunction) {
821 return "ptr<" + std::string(address_space_to_str(addressSpace)) +
", " +
822 to_wgsl_type(context,
type, layout) +
'>';
828 case Builtin::kVertexIndex:
829 return "@builtin(vertex_index)";
830 case Builtin::kInstanceIndex:
831 return "@builtin(instance_index)";
832 case Builtin::kPosition:
833 return "@builtin(position)";
834 case Builtin::kLastFragColor:
836 case Builtin::kFrontFacing:
837 return "@builtin(front_facing)";
838 case Builtin::kSampleIndex:
839 return "@builtin(sample_index)";
840 case Builtin::kFragDepth:
841 return "@builtin(frag_depth)";
842 case Builtin::kSampleMask:
843 case Builtin::kSampleMaskIn:
844 return "@builtin(sample_mask)";
845 case Builtin::kLocalInvocationId:
846 return "@builtin(local_invocation_id)";
847 case Builtin::kLocalInvocationIndex:
848 return "@builtin(local_invocation_index)";
849 case Builtin::kGlobalInvocationId:
850 return "@builtin(global_invocation_id)";
851 case Builtin::kWorkgroupId:
852 return "@builtin(workgroup_id)";
853 case Builtin::kNumWorkgroups:
854 return "@builtin(num_workgroups)";
860 return "unsupported";
866 case Builtin::kVertexIndex:
868 case Builtin::kInstanceIndex:
870 case Builtin::kPosition:
872 case Builtin::kLastFragColor:
874 case Builtin::kFrontFacing:
876 case Builtin::kSampleIndex:
878 case Builtin::kFragDepth:
880 case Builtin::kSampleMask:
882 case Builtin::kSampleMaskIn:
884 case Builtin::kLocalInvocationId:
886 case Builtin::kLocalInvocationIndex:
888 case Builtin::kGlobalInvocationId:
890 case Builtin::kWorkgroupId:
892 case Builtin::kNumWorkgroups:
899 return "unsupported";
906std::optional<std::string_view> needs_builtin_type_conversion(
const Variable& v) {
907 switch (v.layout().fBuiltin) {
921std::optional<WGSLCodeGenerator::Builtin> builtin_from_sksl_name(
int builtin) {
927 return Builtin::kPosition;
929 return Builtin::kVertexIndex;
931 return Builtin::kInstanceIndex;
933 return Builtin::kLastFragColor;
939 return Builtin::kFrontFacing;
941 return Builtin::kSampleMaskIn;
943 return Builtin::kSampleMask;
945 return Builtin::kNumWorkgroups;
947 return Builtin::kWorkgroupId;
949 return Builtin::kLocalInvocationId;
951 return Builtin::kGlobalInvocationId;
953 return Builtin::kLocalInvocationIndex;
965 case Delim::kSemicolon:
977class FunctionDependencyResolver :
public ProgramVisitor {
979 using Deps = WGSLFunctionDependencies;
982 FunctionDependencyResolver(
const Program* p,
983 const FunctionDeclaration* f,
984 DepsMap* programDependencyMap)
985 : fProgram(
p), fFunction(
f), fDependencyMap(programDependencyMap) {}
988 fDeps = WGSLFunctionDependency::kNone;
989 this->visit(*fProgram);
994 bool visitProgramElement(
const ProgramElement& p)
override {
996 if (
p.is<FunctionDefinition>() && &
p.as<FunctionDefinition>().declaration() == fFunction) {
1003 bool visitExpression(
const Expression& e)
override {
1004 if (
e.is<VariableReference>()) {
1005 const VariableReference& v =
e.as<VariableReference>();
1006 if (v.variable()->storage() == Variable::Storage::kGlobal) {
1007 ModifierFlags
flags = v.variable()->modifierFlags();
1009 fDeps |= WGSLFunctionDependency::kPipelineInputs;
1012 fDeps |= WGSLFunctionDependency::kPipelineOutputs;
1015 }
else if (
e.is<FunctionCall>()) {
1019 const FunctionCall& callee =
e.as<FunctionCall>();
1022 Deps* found = fDependencyMap->find(&callee.function());
1029 fDependencyMap->set(fFunction, fDeps);
1033 FunctionDependencyResolver resolver(fProgram, &callee.function(), fDependencyMap);
1034 Deps calleeDeps = resolver.resolve();
1038 fDependencyMap->set(&callee.function(), calleeDeps);
1041 fDeps |= calleeDeps;
1047 const Program*
const fProgram;
1048 const FunctionDeclaration*
const fFunction;
1049 DepsMap*
const fDependencyMap;
1050 Deps fDeps = WGSLFunctionDependency::kNone;
1055WGSLCodeGenerator::ProgramRequirements resolve_program_requirements(
const Program* program) {
1056 WGSLCodeGenerator::ProgramRequirements requirements;
1058 for (
const ProgramElement* e : program->elements()) {
1059 switch (
e->kind()) {
1060 case ProgramElement::Kind::kFunction: {
1061 const FunctionDeclaration& decl =
e->as<FunctionDefinition>().declaration();
1063 FunctionDependencyResolver resolver(program, &decl, &requirements.fDependencies);
1064 requirements.fDependencies.set(&decl, resolver.resolve());
1067 case ProgramElement::Kind::kGlobalVar: {
1068 const GlobalVarDeclaration& decl =
e->as<GlobalVarDeclaration>();
1069 if (decl.varDeclaration().var()->modifierFlags().isPixelLocal()) {
1070 requirements.fPixelLocalExtension =
true;
1079 return requirements;
1082void collect_pipeline_io_vars(
const Program* program,
1085 for (
const ProgramElement* e : program->elements()) {
1086 if (
e->is<GlobalVarDeclaration>()) {
1087 const Variable* v =
e->as<GlobalVarDeclaration>().varDeclaration().var();
1088 if (v->modifierFlags() & ioType) {
1091 }
else if (
e->is<InterfaceBlock>()) {
1092 const Variable* v =
e->as<InterfaceBlock>().var();
1093 if (v->modifierFlags() & ioType) {
1100bool is_in_global_uniforms(
const Variable& var) {
1102 return var.modifierFlags().isUniform() &&
1103 !var.type().isOpaque() &&
1104 !var.interfaceBlock();
1119 virtual std::string
store(
const std::string& value) = 0;
1132 std::string
store(
const std::string& value)
override {
1133 return fName +
" = " +
value +
";";
1150 std::string
store(
const std::string& value)
override {
1151 return fName +
" = " +
value +
";";
1184 for (int8_t component : fComponents) {
1186 used[component] =
true;
1190 for (
int index = 0; index < fullSlotCount; ++index) {
1198 fReintegrationSwizzle.
resize(fullSlotCount);
1199 for (
int index = 0; index < fComponents.
size(); ++index) {
1200 fReintegrationSwizzle[fComponents[index]] = index;
1203 int originalValueComponentIndex = fComponents.
size();
1204 for (
int index = 0; index < fullSlotCount; ++index) {
1206 fReintegrationSwizzle[index] = originalValueComponentIndex++;
1215 std::string
store(
const std::string& value)
override {
1217 std::string
result = fName;
1220 if (fUntouchedComponents.
empty()) {
1228 result += to_wgsl_type(fContext, fType);
1257 this->preprocessProgram();
1261 this->writeEnables();
1262 this->writeStageInputStruct();
1263 this->writeStageOutputStruct();
1264 this->writeUniformsAndBuffers();
1265 this->writeNonBlockUniformsForTests();
1273 this->writeProgramElement(*e);
1287 this->writeEntryPoint(*mainFunc);
1294 this->writeUniformPolyfills();
1299void WGSLCodeGenerator::writeUniformPolyfills() {
1301 if (fFieldPolyfillMap.
empty()) {
1310 fFieldPolyfillMap.
foreach([&](
const FieldPolyfillMap::Pair& pair) {
1314 std::sort(orderedFields.
begin(),
1315 orderedFields.
end(),
1316 [](
const FieldPolyfillMap::Pair*
a,
const FieldPolyfillMap::Pair*
b) {
1317 return a->second.fReplacementName < b->second.fReplacementName;
1321 bool writtenUniformMatrixPolyfill[5][5] = {};
1322 bool writtenUniformRowPolyfill[5] = {};
1323 bool anyFieldAccessed =
false;
1324 for (
const FieldPolyfillMap::Pair* pair : orderedFields) {
1325 const auto& [field,
info] = *pair;
1326 const Type* fieldType = field->fType;
1327 const Layout* fieldLayout = &field->fLayout;
1329 if (
info.fIsArray) {
1330 fieldType = &fieldType->componentType();
1331 if (!writtenArrayElementPolyfill.
contains(fieldType)) {
1332 writtenArrayElementPolyfill.
add(fieldType);
1333 this->write(
"struct _skArrayElement_");
1334 this->write(fieldType->abbreviatedName());
1335 this->writeLine(
" {");
1337 if (
info.fIsMatrix) {
1339 this->write(
" e : _skMatrix");
1340 this->write(std::to_string(fieldType->columns()));
1341 this->writeLine(std::to_string(fieldType->rows()));
1344 this->write(
" @size(16) e : ");
1345 this->writeLine(to_wgsl_type(
fContext, *fieldType, fieldLayout));
1347 this->writeLine(
"};");
1351 if (
info.fIsMatrix) {
1355 int c = fieldType->columns();
1356 int r = fieldType->rows();
1357 if (!writtenUniformRowPolyfill[r]) {
1358 writtenUniformRowPolyfill[r] =
true;
1360 this->write(
"struct _skRow");
1361 this->write(std::to_string(r));
1362 this->writeLine(
" {");
1363 this->write(
" @size(16) r : vec");
1364 this->write(std::to_string(r));
1366 this->write(to_wgsl_type(
fContext, fieldType->componentType(), fieldLayout));
1367 this->writeLine(
">");
1368 this->writeLine(
"};");
1371 if (!writtenUniformMatrixPolyfill[c][r]) {
1372 writtenUniformMatrixPolyfill[c][r] =
true;
1374 this->write(
"struct _skMatrix");
1375 this->write(std::to_string(c));
1376 this->write(std::to_string(r));
1377 this->writeLine(
" {");
1378 this->write(
" c : array<_skRow");
1379 this->write(std::to_string(r));
1381 this->write(std::to_string(c));
1382 this->writeLine(
">");
1383 this->writeLine(
"};");
1388 if (!
info.fWasAccessed) {
1391 anyFieldAccessed =
true;
1392 this->write(
"var<private> ");
1393 this->write(
info.fReplacementName);
1396 const Type& interfaceBlockType =
info.fInterfaceBlock->var()->type();
1397 if (interfaceBlockType.isArray()) {
1398 this->write(
"array<");
1399 this->write(to_wgsl_type(
fContext, *field->fType, fieldLayout));
1401 this->write(std::to_string(interfaceBlockType.columns()));
1404 this->write(to_wgsl_type(
fContext, *field->fType, fieldLayout));
1406 this->writeLine(
";");
1411 if (!anyFieldAccessed) {
1415 this->writeLine(
"fn _skInitializePolyfilledUniforms() {");
1418 for (
const FieldPolyfillMap::Pair* pair : orderedFields) {
1420 const auto& [field,
info] = *pair;
1421 if (!
info.fWasAccessed) {
1426 std::string_view instanceName =
info.fInterfaceBlock->instanceName();
1427 const Type& interfaceBlockType =
info.fInterfaceBlock->var()->type();
1428 if (instanceName.empty()) {
1429 instanceName = fInterfaceBlockNameMap[&interfaceBlockType.componentType()];
1434 int numIBElements = interfaceBlockType.isArray() ? interfaceBlockType.columns() : 1;
1435 for (
int ibIdx = 0; ibIdx < numIBElements; ++ibIdx) {
1436 this->write(
info.fReplacementName);
1437 if (interfaceBlockType.isArray()) {
1439 this->write(std::to_string(ibIdx));
1444 const Type* fieldType = field->fType;
1445 const Layout* fieldLayout = &field->fLayout;
1447 int numArrayElements;
1448 if (
info.fIsArray) {
1449 this->write(to_wgsl_type(
fContext, *fieldType, fieldLayout));
1451 numArrayElements = fieldType->columns();
1452 fieldType = &fieldType->componentType();
1454 numArrayElements = 1;
1458 for (
int arrayIdx = 0; arrayIdx < numArrayElements; arrayIdx++) {
1459 this->write(arraySeparator());
1461 std::string fieldName{instanceName};
1462 if (interfaceBlockType.isArray()) {
1464 fieldName += std::to_string(ibIdx);
1468 fieldName += this->assembleName(field->fName);
1470 if (
info.fIsArray) {
1472 fieldName += std::to_string(arrayIdx);
1476 if (
info.fIsMatrix) {
1477 this->write(to_wgsl_type(
fContext, *fieldType, fieldLayout));
1479 int numColumns = fieldType->columns();
1481 for (
int column = 0; column < numColumns; column++) {
1482 this->write(matrixSeparator());
1483 this->write(fieldName);
1485 this->write(std::to_string(column));
1490 this->write(fieldName);
1494 if (
info.fIsArray) {
1498 this->writeLine(
";");
1503 this->writeLine(
"}");
1507void WGSLCodeGenerator::preprocessProgram() {
1508 fRequirements = resolve_program_requirements(&
fProgram);
1513void WGSLCodeGenerator::write(std::string_view
s) {
1517#if defined(SK_DEBUG) || defined(SKSL_STANDALONE)
1519 for (
int i = 0; i < fIndentation; i++) {
1525 fAtLineStart =
false;
1528void WGSLCodeGenerator::writeLine(std::string_view
s) {
1531 fAtLineStart =
true;
1534void WGSLCodeGenerator::finishLine() {
1535 if (!fAtLineStart) {
1540std::string WGSLCodeGenerator::assembleName(std::string_view
name) {
1543 return "_skAnonymous" + std::to_string(fScratchCount++);
1548 is_reserved_word(
name))
1549 ? std::string(
"R_") + std::string(
name)
1553void WGSLCodeGenerator::writeVariableDecl(
const Layout& layout,
1555 std::string_view
name,
1556 Delimiter delimiter) {
1557 this->write(this->assembleName(
name));
1558 this->write(
": " + to_wgsl_type(
fContext,
type, &layout));
1559 this->writeLine(delimiter_to_str(delimiter));
1562void WGSLCodeGenerator::writePipelineIODeclaration(
const Layout& layout,
1564 std::string_view
name,
1565 Delimiter delimiter) {
1578 if (layout.fLocation >= 0) {
1579 this->writeUserDefinedIODecl(layout,
type,
name, delimiter);
1582 if (layout.fBuiltin >= 0) {
1588 auto builtin = builtin_from_sksl_name(layout.fBuiltin);
1589 if (builtin.has_value()) {
1590 this->writeBuiltinIODecl(
type,
name, *builtin, delimiter);
1597void WGSLCodeGenerator::writeUserDefinedIODecl(
const Layout& layout,
1599 std::string_view
name,
1600 Delimiter delimiter) {
1601 this->write(
"@location(" + std::to_string(layout.fLocation) +
") ");
1605 this->write(
"@blend_src(" + std::to_string(layout.fIndex) +
") ");
1610 if (
type.isInteger() || (
type.isVector() &&
type.componentType().isInteger())) {
1611 this->write(
"@interpolate(flat) ");
1614 this->writeVariableDecl(layout,
type,
name, delimiter);
1617void WGSLCodeGenerator::writeBuiltinIODecl(
const Type&
type,
1618 std::string_view
name,
1620 Delimiter delimiter) {
1621 this->write(wgsl_builtin_name(builtin));
1623 this->write(this->assembleName(
name));
1625 this->write(wgsl_builtin_type(builtin));
1626 this->writeLine(delimiter_to_str(delimiter));
1629void WGSLCodeGenerator::writeFunction(
const FunctionDefinition& f) {
1630 const FunctionDeclaration& decl =
f.declaration();
1631 fHasUnconditionalReturn =
false;
1632 fConditionalScopeDepth = 0;
1635 fAtFunctionScope =
true;
1643 paramNeedsDedicatedStorage.
push_back_n(decl.parameters().size(),
true);
1645 for (
size_t index = 0; index < decl.parameters().
size(); ++index) {
1646 const Variable& param = *decl.parameters()[index];
1647 if (param.type().isOpaque() || param.name().empty()) {
1649 paramNeedsDedicatedStorage[index] =
false;
1658 paramNeedsDedicatedStorage[index] =
false;
1662 this->writeFunctionDeclaration(decl, paramNeedsDedicatedStorage);
1663 this->writeLine(
" {");
1669 for (
size_t index = 0; index < decl.parameters().
size(); ++index) {
1670 if (paramNeedsDedicatedStorage[index]) {
1671 const Variable& param = *decl.parameters()[index];
1672 this->write(
"var ");
1673 this->write(this->assembleName(param.mangledName()));
1674 this->write(
" = _skParam");
1675 this->write(std::to_string(index));
1676 this->writeLine(
";");
1680 this->writeBlock(
f.body()->as<
Block>());
1683 SkASSERT(fConditionalScopeDepth == 0);
1684 if (!fHasUnconditionalReturn && !
f.declaration().returnType().isVoid()) {
1685 this->write(
"return ");
1686 this->write(to_wgsl_type(
fContext,
f.declaration().returnType()));
1687 this->writeLine(
"();");
1691 this->writeLine(
"}");
1694 fAtFunctionScope =
false;
1697void WGSLCodeGenerator::writeFunctionDeclaration(
const FunctionDeclaration& decl,
1700 if (decl.isMain()) {
1701 this->write(
"_skslMain(");
1703 this->write(this->assembleName(decl.mangledName()));
1707 if (this->writeFunctionDependencyParams(decl)) {
1710 for (
size_t index = 0; index < decl.parameters().
size(); ++index) {
1711 this->write(separator());
1713 const Variable& param = *decl.parameters()[index];
1714 if (param.type().isOpaque()) {
1715 SkASSERT(!paramNeedsDedicatedStorage[index]);
1716 if (param.type().isSampler()) {
1718 this->write(param.name());
1719 this->write(kTextureSuffix);
1720 this->write(
": texture_2d<f32>, ");
1721 this->write(param.name());
1722 this->write(kSamplerSuffix);
1723 this->write(
": sampler");
1726 this->write(param.name());
1728 this->write(to_wgsl_type(
fContext, param.type(), ¶m.layout()));
1731 if (paramNeedsDedicatedStorage[index] || param.name().empty()) {
1735 this->write(
"_skParam");
1736 this->write(std::to_string(index));
1739 this->write(this->assembleName(param.name()));
1744 this->write(to_ptr_type(
fContext, param.type(), ¶m.layout()));
1746 this->write(to_wgsl_type(
fContext, param.type(), ¶m.layout()));
1751 if (!decl.returnType().isVoid()) {
1752 this->write(
" -> ");
1753 this->write(to_wgsl_type(
fContext, decl.returnType()));
1757void WGSLCodeGenerator::writeEntryPoint(
const FunctionDefinition&
main) {
1761#if defined(SKSL_STANDALONE)
1766 this->writeLine(
"@fragment fn main(@location(0) _coords: vec2<f32>) -> "
1767 "@location(0) vec4<f32> {");
1769 this->writeLine(
"return _skslMain(_coords);");
1771 this->writeLine(
"}");
1781 this->write(
"@vertex");
1783 this->write(
"@fragment");
1785 this->write(
"@compute @workgroup_size(");
1786 this->write(std::to_string(fLocalSizeX));
1788 this->write(std::to_string(fLocalSizeY));
1790 this->write(std::to_string(fLocalSizeZ));
1797 this->write(
" fn main(");
1799 if (this->needsStageInputStruct()) {
1800 this->write(
"_stageIn: ");
1801 this->write(pipeline_struct_prefix(programKind));
1805 if (this->needsStageOutputStruct()) {
1806 this->write(
") -> ");
1807 this->write(pipeline_struct_prefix(programKind));
1808 this->writeLine(
"Out {");
1810 this->writeLine(
") {");
1814 for (
const auto& [field,
info] : fFieldPolyfillMap) {
1815 if (
info.fWasAccessed) {
1816 this->writeLine(
"_skInitializePolyfilledUniforms();");
1821 if (this->needsStageOutputStruct()) {
1822 this->write(
"var _stageOut: ");
1823 this->write(pipeline_struct_prefix(programKind));
1824 this->writeLine(
"Out;");
1827#if defined(SKSL_STANDALONE)
1835 this->write(
"_stageOut.sk_FragColor = ");
1841 this->write(
"_skslMain(");
1845 if (*deps & WGSLFunctionDependency::kPipelineInputs) {
1846 this->write(separator());
1847 this->write(
"_stageIn");
1849 if (*deps & WGSLFunctionDependency::kPipelineOutputs) {
1850 this->write(separator());
1851 this->write(
"&_stageOut");
1855#if defined(SKSL_STANDALONE)
1856 if (
const Variable* v =
main.declaration().getMainCoordsParameter()) {
1863 type.description());
1866 this->write(separator());
1867 this->write(
"/*fragcoord*/ vec2<f32>()");
1871 this->writeLine(
");");
1873 if (this->needsStageOutputStruct()) {
1875 this->writeLine(
"return _stageOut;");
1879 this->writeLine(
"}");
1882void WGSLCodeGenerator::writeStatement(
const Statement&
s) {
1884 case Statement::Kind::kBlock:
1885 this->writeBlock(
s.as<
Block>());
1887 case Statement::Kind::kBreak:
1888 this->writeLine(
"break;");
1890 case Statement::Kind::kContinue:
1891 this->writeLine(
"continue;");
1893 case Statement::Kind::kDiscard:
1894 this->writeLine(
"discard;");
1896 case Statement::Kind::kDo:
1897 this->writeDoStatement(
s.as<DoStatement>());
1899 case Statement::Kind::kExpression:
1900 this->writeExpressionStatement(*
s.as<ExpressionStatement>().expression());
1902 case Statement::Kind::kFor:
1903 this->writeForStatement(
s.as<ForStatement>());
1905 case Statement::Kind::kIf:
1906 this->writeIfStatement(
s.as<IfStatement>());
1908 case Statement::Kind::kNop:
1909 this->writeLine(
";");
1911 case Statement::Kind::kReturn:
1912 this->writeReturnStatement(
s.as<ReturnStatement>());
1914 case Statement::Kind::kSwitch:
1915 this->writeSwitchStatement(
s.as<SwitchStatement>());
1917 case Statement::Kind::kSwitchCase:
1918 SkDEBUGFAIL(
"switch-case statements should only be present inside a switch");
1920 case Statement::Kind::kVarDeclaration:
1921 this->writeVarDeclaration(
s.as<VarDeclaration>());
1926void WGSLCodeGenerator::writeStatements(
const StatementArray& statements) {
1927 for (
const auto&
s : statements) {
1928 if (!
s->isEmpty()) {
1929 this->writeStatement(*
s);
1935void WGSLCodeGenerator::writeBlock(
const Block&
b) {
1938 bool isScope =
b.isScope() ||
b.isEmpty();
1940 this->writeLine(
"{");
1943 this->writeStatements(
b.children());
1946 this->writeLine(
"}");
1950void WGSLCodeGenerator::writeExpressionStatement(
const Expression& expr) {
1955 (void)this->assembleExpression(expr, Precedence::kStatement);
1958void WGSLCodeGenerator::writeDoStatement(
const DoStatement&
s) {
1967 ++fConditionalScopeDepth;
1972 this->writeLine(
"loop {");
1974 this->writeStatement(*
s.statement());
1977 this->writeLine(
"continuing {");
1979 std::string breakIfExpr = this->assembleExpression(*invertedTestExpr, Precedence::kExpression);
1980 this->write(
"break if ");
1981 this->write(breakIfExpr);
1982 this->writeLine(
";");
1984 this->writeLine(
"}");
1986 this->writeLine(
"}");
1988 --fConditionalScopeDepth;
1991void WGSLCodeGenerator::writeForStatement(
const ForStatement&
s) {
2001 ++fConditionalScopeDepth;
2003 if (
s.initializer()) {
2004 this->writeLine(
"{");
2006 this->writeStatement(*
s.initializer());
2010 this->writeLine(
"loop {");
2013 if (
s.unrollInfo()) {
2014 if (
s.unrollInfo()->fCount <= 0) {
2028 this->writeStatement(*
s.statement());
2030 this->writeLine(
"continuing {");
2034 this->writeExpressionStatement(*
s.next());
2042 std::string breakIfExpr =
2043 this->assembleExpression(*invertedTestExpr, Precedence::kExpression);
2044 this->write(
"break if ");
2045 this->write(breakIfExpr);
2046 this->writeLine(
";");
2050 this->writeLine(
"}");
2067 std::string testExpr = this->assembleExpression(*
s.test(), Precedence::kExpression);
2069 this->write(testExpr);
2070 this->writeLine(
" {");
2073 this->writeStatement(*
s.statement());
2077 this->writeLine(
"} else {");
2080 this->writeLine(
"break;");
2083 this->writeLine(
"}");
2086 this->writeStatement(*
s.statement());
2091 this->writeLine(
"continuing {");
2093 this->writeExpressionStatement(*
s.next());
2096 this->writeLine(
"}");
2102 this->writeLine(
"}");
2104 if (
s.initializer()) {
2107 this->writeLine(
"}");
2110 --fConditionalScopeDepth;
2113void WGSLCodeGenerator::writeIfStatement(
const IfStatement&
s) {
2114 ++fConditionalScopeDepth;
2116 std::string testExpr = this->assembleExpression(*
s.test(), Precedence::kExpression);
2118 this->write(testExpr);
2119 this->writeLine(
" {");
2121 this->writeStatement(*
s.ifTrue());
2125 this->writeLine(
"} else {");
2127 this->writeStatement(*
s.ifFalse());
2131 this->writeLine(
"}");
2133 --fConditionalScopeDepth;
2136void WGSLCodeGenerator::writeReturnStatement(
const ReturnStatement&
s) {
2137 fHasUnconditionalReturn |= (fConditionalScopeDepth == 0);
2139 std::string expr =
s.expression()
2140 ? this->assembleExpression(*
s.expression(), Precedence::kExpression)
2142 this->write(
"return ");
2149 for (
const SwitchCase*
const sc : cases) {
2150 this->write(separator());
2151 if (sc->isDefault()) {
2152 this->write(
"default");
2154 this->write(std::to_string(sc->value()));
2160 if (!cases.
empty()) {
2162 SkASSERT(std::all_of(cases.
begin(), std::prev(cases.
end()), [](
const SwitchCase* sc) {
2163 return sc->statement()->isEmpty();
2167 this->write(
"case ");
2168 this->writeSwitchCaseList(cases);
2169 this->writeLine(
" {");
2173 this->writeStatement(*cases.
back()->statement());
2177 this->writeLine(
"}");
2182 std::string_view switchValue) {
2184 if (cases.
size() < 2) {
2185 this->writeSwitchCases(cases);
2190 this->write(
"case ");
2191 this->writeSwitchCaseList(cases);
2192 this->writeLine(
" {");
2196 const size_t secondToLastCaseIndex = cases.
size() - 2;
2197 const size_t lastCaseIndex = cases.
size() - 1;
2199 for (
size_t index = 0; index < cases.
size(); ++index) {
2200 const SwitchCase& sc = *cases[index];
2201 if (index < lastCaseIndex) {
2208 this->write(fallthroughVar);
2209 this->write(
" || ");
2211 this->write(switchValue);
2212 this->write(
" == ");
2213 this->write(std::to_string(sc.value()));
2214 this->writeLine(
" {");
2223 this->writeStatement(*sc.statement());
2226 if (index < secondToLastCaseIndex) {
2231 this->write(fallthroughVar);
2232 this->write(
" = true; ");
2234 this->writeLine(
"// fallthrough");
2237 this->writeLine(
"}");
2243 this->writeStatement(*sc.statement());
2249 this->writeLine(
"}");
2252void WGSLCodeGenerator::writeSwitchStatement(
const SwitchStatement&
s) {
2267 std::string valueExpr = this->writeNontrivialScratchLet(*
s.value(), Precedence::kExpression);
2268 this->write(
"switch ");
2269 this->write(valueExpr);
2270 this->writeLine(
" {");
2276 bool previousCaseFellThrough =
false;
2277 bool foundNativeDefault =
false;
2278 [[maybe_unused]]
bool foundFallthroughDefault =
false;
2280 const int lastSwitchCaseIdx =
s.cases().size() - 1;
2281 for (
int index = 0; index <= lastSwitchCaseIdx; ++index) {
2282 const SwitchCase& sc =
s.cases()[index]->as<SwitchCase>();
2284 if (sc.statement()->isEmpty()) {
2287 if (previousCaseFellThrough) {
2289 foundFallthroughDefault |= sc.isDefault();
2292 foundNativeDefault |= sc.isDefault();
2299 if (previousCaseFellThrough) {
2302 foundFallthroughDefault |= sc.isDefault();
2304 this->writeEmulatedSwitchFallthroughCases(fallthroughCases, valueExpr);
2305 fallthroughCases.
clear();
2309 previousCaseFellThrough =
false;
2313 foundNativeDefault |= sc.isDefault();
2315 this->writeSwitchCases(nativeCases);
2316 nativeCases.
clear();
2325 nativeCases.
clear();
2328 foundFallthroughDefault |= sc.isDefault();
2329 previousCaseFellThrough =
true;
2333 this->writeSwitchCases(nativeCases);
2334 nativeCases.
clear();
2336 this->writeEmulatedSwitchFallthroughCases(fallthroughCases, valueExpr);
2337 fallthroughCases.
clear();
2340 if (!foundNativeDefault && !foundFallthroughDefault) {
2341 this->writeLine(
"case default {}");
2345 this->writeLine(
"}");
2348void WGSLCodeGenerator::writeVarDeclaration(
const VarDeclaration& varDecl) {
2349 std::string initialValue =
2350 varDecl.value() ? this->assembleExpression(*varDecl.value(), Precedence::kAssignment)
2353 if (varDecl.var()->modifierFlags().isConst()) {
2355 SkASSERTF(varDecl.value(),
"a constant variable must specify a value");
2360 this->write(
"var ");
2362 this->write(this->assembleName(varDecl.var()->mangledName()));
2364 this->write(to_wgsl_type(
fContext, varDecl.var()->type(), &varDecl.var()->layout()));
2366 if (varDecl.value()) {
2368 this->write(initialValue);
2374std::unique_ptr<WGSLCodeGenerator::LValue> WGSLCodeGenerator::makeLValue(
const Expression& e) {
2375 if (
e.is<VariableReference>()) {
2376 return std::make_unique<PointerLValue>(
2377 this->variableReferenceNameForLValue(
e.as<VariableReference>()));
2379 if (
e.is<FieldAccess>()) {
2380 return std::make_unique<PointerLValue>(this->assembleFieldAccess(
e.as<FieldAccess>()));
2382 if (
e.is<IndexExpression>()) {
2383 const IndexExpression& idx =
e.as<IndexExpression>();
2384 if (idx.base()->type().isVector()) {
2386 if (std::unique_ptr<Expression> rewrite =
2388 return std::make_unique<VectorComponentLValue>(
2389 this->assembleExpression(*rewrite, Precedence::kAssignment));
2391 return std::make_unique<VectorComponentLValue>(this->assembleIndexExpression(idx));
2394 return std::make_unique<PointerLValue>(this->assembleIndexExpression(idx));
2397 if (
e.is<Swizzle>()) {
2398 const Swizzle& swizzle =
e.as<Swizzle>();
2399 if (swizzle.components().size() == 1) {
2400 return std::make_unique<VectorComponentLValue>(this->assembleSwizzle(swizzle));
2402 return std::make_unique<SwizzleLValue>(
2404 this->assembleExpression(*swizzle.base(), Precedence::kAssignment),
2405 swizzle.base()->type(),
2406 swizzle.components());
2414std::string WGSLCodeGenerator::assembleExpression(
const Expression& e,
2415 Precedence parentPrecedence) {
2417 case Expression::Kind::kBinary:
2418 return this->assembleBinaryExpression(
e.as<BinaryExpression>(), parentPrecedence);
2420 case Expression::Kind::kConstructorCompound:
2421 return this->assembleConstructorCompound(
e.as<ConstructorCompound>());
2423 case Expression::Kind::kConstructorArrayCast:
2426 return this->assembleExpression(*
e.as<ConstructorArrayCast>().argument(),
2429 case Expression::Kind::kConstructorArray:
2430 case Expression::Kind::kConstructorCompoundCast:
2431 case Expression::Kind::kConstructorScalarCast:
2432 case Expression::Kind::kConstructorSplat:
2433 case Expression::Kind::kConstructorStruct:
2434 return this->assembleAnyConstructor(
e.asAnyConstructor());
2436 case Expression::Kind::kConstructorDiagonalMatrix:
2437 return this->assembleConstructorDiagonalMatrix(
e.as<ConstructorDiagonalMatrix>());
2439 case Expression::Kind::kConstructorMatrixResize:
2440 return this->assembleConstructorMatrixResize(
e.as<ConstructorMatrixResize>());
2442 case Expression::Kind::kEmpty:
2445 case Expression::Kind::kFieldAccess:
2446 return this->assembleFieldAccess(
e.as<FieldAccess>());
2448 case Expression::Kind::kFunctionCall:
2449 return this->assembleFunctionCall(
e.as<FunctionCall>(), parentPrecedence);
2451 case Expression::Kind::kIndex:
2452 return this->assembleIndexExpression(
e.as<IndexExpression>());
2454 case Expression::Kind::kLiteral:
2455 return this->assembleLiteral(
e.as<Literal>());
2457 case Expression::Kind::kPrefix:
2458 return this->assemblePrefixExpression(
e.as<PrefixExpression>(), parentPrecedence);
2460 case Expression::Kind::kPostfix:
2461 return this->assemblePostfixExpression(
e.as<PostfixExpression>(), parentPrecedence);
2463 case Expression::Kind::kSetting:
2464 return this->assembleExpression(*
e.as<Setting>().toLiteral(
fCaps), parentPrecedence);
2466 case Expression::Kind::kSwizzle:
2467 return this->assembleSwizzle(
e.as<Swizzle>());
2469 case Expression::Kind::kTernary:
2470 return this->assembleTernaryExpression(
e.as<TernaryExpression>(), parentPrecedence);
2472 case Expression::Kind::kVariableReference:
2473 return this->assembleVariableReference(
e.as<VariableReference>());
2476 SkDEBUGFAILF(
"unsupported expression:\n%s",
e.description().c_str());
2499 switch (op.
kind()) {
2518bool WGSLCodeGenerator::binaryOpNeedsComponentwiseMatrixPolyfill(
const Type&
left,
2521 switch (op.kind()) {
2524 if (
left.isMatrix() &&
right.isMatrix()) {
2533 return (
left.isMatrix() &&
right.isScalar()) ||
2541std::string WGSLCodeGenerator::assembleBinaryExpression(
const BinaryExpression&
b,
2542 Precedence parentPrecedence) {
2543 return this->assembleBinaryExpression(*
b.left(),
b.getOperator(), *
b.right(),
b.type(),
2547std::string WGSLCodeGenerator::assembleBinaryExpression(
const Expression&
left,
2549 const Expression&
right,
2550 const Type& resultType,
2551 Precedence parentPrecedence) {
2573 expr = this->writeScratchVar(resultType);
2575 std::string leftExpr = this->assembleExpression(
left, Precedence::kExpression);
2577 this->write(leftExpr);
2578 this->writeLine(
" {");
2581 std::string rightExpr = this->assembleExpression(
right, Precedence::kAssignment);
2584 this->write(rightExpr);
2585 this->writeLine(
";");
2588 this->writeLine(
"} else {");
2592 this->writeLine(
" = false;");
2595 this->writeLine(
"}");
2611 expr = this->writeScratchVar(resultType);
2613 std::string leftExpr = this->assembleExpression(
left, Precedence::kExpression);
2615 this->write(leftExpr);
2616 this->writeLine(
" {");
2620 this->writeLine(
" = true;");
2623 this->writeLine(
"} else {");
2626 std::string rightExpr = this->assembleExpression(
right, Precedence::kAssignment);
2629 this->write(rightExpr);
2630 this->writeLine(
";");
2633 this->writeLine(
"}");
2640 this->assembleExpression(
left, Precedence::kStatement);
2643 return this->assembleExpression(
right, parentPrecedence);
2647 if (op.isAssignment()) {
2648 std::unique_ptr<LValue> lvalue = this->makeLValue(
left);
2655 expr = this->assembleExpression(
right, Precedence::kAssignment);
2658 op = op.removeAssignment();
2660 std::string lhs = lvalue->load();
2661 std::string rhs = this->assembleExpression(
right, op.getBinaryPrecedence());
2663 if (this->binaryOpNeedsComponentwiseMatrixPolyfill(
left.type(),
right.type(), op)) {
2665 rhs = this->writeScratchLet(rhs);
2668 expr = this->assembleComponentwiseMatrixBinary(
left.type(),
right.type(),
2676 this->writeLine(lvalue->store(expr));
2679 return lvalue->load();
2682 if (op.isEquality()) {
2683 return this->assembleEqualityExpression(
left,
right, op, parentPrecedence);
2686 Precedence precedence = op.getBinaryPrecedence();
2687 bool needParens = precedence >= parentPrecedence;
2689 precedence = Precedence::kParentheses;
2704 std::string lhs = this->assembleExpression(
left, precedence);
2705 std::string rhs = this->assembleExpression(
right, precedence);
2707 if (this->binaryOpNeedsComponentwiseMatrixPolyfill(
left.type(),
right.type(), op)) {
2709 lhs = this->writeScratchLet(lhs);
2712 rhs = this->writeScratchLet(rhs);
2715 expr += this->assembleComponentwiseMatrixBinary(
left.type(),
right.type(), lhs, rhs, op);
2717 if (bothSidesConstant) {
2718 lhs = this->writeScratchLet(lhs);
2731std::string WGSLCodeGenerator::assembleFieldAccess(
const FieldAccess& f) {
2732 const Field* field = &
f.base()->type().fields()[
f.fieldIndex()];
2735 if (FieldPolyfillInfo* polyfillInfo = fFieldPolyfillMap.
find(field)) {
2740 polyfillInfo->fWasAccessed =
true;
2744 const Expression*
base =
f.base().get();
2745 const IndexExpression* indexExpr =
nullptr;
2746 if (
base->is<IndexExpression>()) {
2747 indexExpr = &
base->as<IndexExpression>();
2748 base = indexExpr->base().get();
2752 expr = polyfillInfo->fReplacementName;
2757 expr += this->assembleExpression(*indexExpr->index(), Precedence::kSequence);
2763 switch (
f.ownerKind()) {
2764 case FieldAccess::OwnerKind::kDefault:
2765 expr = this->assembleExpression(*
f.base(), Precedence::kPostfix) +
'.';
2768 case FieldAccess::OwnerKind::kAnonymousInterfaceBlock:
2769 if (
f.base()->is<VariableReference>() &&
2771 expr = this->variablePrefix(*
f.base()->as<VariableReference>().variable());
2776 expr += this->assembleName(field->fName);
2787 for (
const std::unique_ptr<Expression>& arg : arguments) {
2795std::string WGSLCodeGenerator::assembleSimpleIntrinsic(std::string_view intrinsicName,
2796 const FunctionCall& call) {
2798 std::string expr = std::string(intrinsicName);
2799 expr.push_back(
'(');
2800 const ExpressionArray&
args = call.arguments();
2803 for (
int index = 0; index <
args.size(); ++index) {
2804 expr += separator();
2806 std::string argument = this->assembleExpression(*
args[index], Precedence::kSequence);
2807 if (
args[index]->
type().isAtomic()) {
2811 }
else if (allConstant && index == 0) {
2813 expr += this->writeScratchLet(argument);
2818 expr.push_back(
')');
2820 if (
call.type().isVoid()) {
2822 this->writeLine(
";");
2823 return std::string();
2825 return this->writeScratchLet(expr);
2829std::string WGSLCodeGenerator::assembleVectorizedIntrinsic(std::string_view intrinsicName,
2830 const FunctionCall& call) {
2834 std::string expr = std::string(intrinsicName);
2835 expr.push_back(
'(');
2838 const ExpressionArray&
args =
call.arguments();
2839 bool returnsVector =
call.type().isVector();
2841 for (
int index = 0; index <
args.size(); ++index) {
2842 expr += separator();
2844 bool vectorize = returnsVector &&
args[index]->type().isScalar();
2847 expr.push_back(
'(');
2851 std::string argument = this->assembleExpression(*
args[index], Precedence::kSequence);
2852 expr += (allConstant && index == 0) ? this->writeScratchLet(argument)
2855 expr.push_back(
')');
2858 expr.push_back(
')');
2860 return this->writeScratchLet(expr);
2863std::string WGSLCodeGenerator::assembleUnaryOpIntrinsic(Operator op,
2864 const FunctionCall& call,
2865 Precedence parentPrecedence) {
2868 bool needParens = Precedence::kPrefix >= parentPrecedence;
2872 expr.push_back(
'(');
2876 expr += this->assembleExpression(*
call.arguments()[0], Precedence::kPrefix);
2879 expr.push_back(
')');
2885std::string WGSLCodeGenerator::assembleBinaryOpIntrinsic(Operator op,
2886 const FunctionCall& call,
2887 Precedence parentPrecedence) {
2890 Precedence precedence = op.getBinaryPrecedence();
2891 bool needParens = precedence >= parentPrecedence ||
2895 expr.push_back(
'(');
2899 std::string argument = this->assembleExpression(*
call.arguments()[0], precedence);
2903 expr += this->assembleExpression(*
call.arguments()[1], precedence);
2906 expr.push_back(
')');
2915std::string WGSLCodeGenerator::assembleOutAssignedIntrinsic(std::string_view intrinsicName,
2916 std::string_view returnField,
2917 std::string_view outField,
2918 const FunctionCall& call) {
2923 std::string expr = std::string(intrinsicName);
2928 std::string argument = this->assembleExpression(*
call.arguments()[0], Precedence::kSequence);
2930 ? this->writeScratchLet(argument) : argument;
2934 expr = this->writeScratchLet(expr);
2938 std::unique_ptr<LValue> lvalue = this->makeLValue(*
call.arguments()[1]);
2942 std::string outValue = expr;
2943 outValue += outField;
2944 this->writeLine(lvalue->store(outValue));
2947 expr += returnField;
2951std::string WGSLCodeGenerator::assemblePartialSampleCall(std::string_view functionName,
2952 const Expression& sampler,
2953 const Expression& coords) {
2958 std::string expr = std::string(functionName) +
'(';
2959 expr += this->assembleExpression(sampler, Precedence::kSequence);
2960 expr += kTextureSuffix;
2962 expr += this->assembleExpression(sampler, Precedence::kSequence);
2963 expr += kSamplerSuffix;
2967 SkASSERT(coords.type().isVector());
2968 if (coords.type().columns() == 3) {
2970 std::string vec3Coords = this->writeScratchLet(coords, Precedence::kMultiplicative);
2971 expr += vec3Coords +
".xy / " + vec3Coords +
".z";
2974 SkASSERT(coords.type().columns() == 2);
2975 expr += this->assembleExpression(coords, Precedence::kSequence);
2981std::string WGSLCodeGenerator::assembleComponentwiseMatrixBinary(
const Type& leftType,
2982 const Type& rightType,
2983 const std::string&
left,
2984 const std::string&
right,
2986 bool leftIsMatrix = leftType.isMatrix();
2987 bool rightIsMatrix = rightType.isMatrix();
2988 const Type& matrixType = leftIsMatrix ? leftType : rightType;
2990 std::string expr = to_wgsl_type(
fContext, matrixType) +
'(';
2992 int columns = matrixType.columns();
2993 for (
int c = 0; c < columns; ++c) {
2994 expr += separator();
2998 expr += std::to_string(c);
3001 expr += op.operatorName();
3003 if (rightIsMatrix) {
3005 expr += std::to_string(c);
3012std::string WGSLCodeGenerator::assembleIntrinsicCall(
const FunctionCall& call,
3014 Precedence parentPrecedence) {
3021 const ExpressionArray& arguments =
call.arguments();
3023 case k_atan_IntrinsicKind: {
3024 const char*
name = (arguments.size() == 1) ?
"atan" :
"atan2";
3025 return this->assembleSimpleIntrinsic(
name, call);
3027 case k_dFdx_IntrinsicKind:
3028 return this->assembleSimpleIntrinsic(
"dpdx", call);
3030 case k_dFdy_IntrinsicKind:
3032 return this->assembleSimpleIntrinsic(
"dpdy", call);
3034 case k_dot_IntrinsicKind: {
3035 if (arguments[0]->
type().isScalar()) {
3038 return this->assembleSimpleIntrinsic(
"dot", call);
3040 case k_equal_IntrinsicKind:
3043 case k_faceforward_IntrinsicKind: {
3044 if (arguments[0]->
type().isScalar()) {
3046 std::string
N = this->writeNontrivialScratchLet(*arguments[0],
3047 Precedence::kAssignment);
3048 return this->writeScratchLet(
3049 "select(-" +
N +
", " +
N +
", " +
3050 this->assembleBinaryExpression(*arguments[1],
3053 arguments[1]->
type(),
3054 Precedence::kRelational) +
3057 return this->assembleSimpleIntrinsic(
"faceForward", call);
3059 case k_frexp_IntrinsicKind:
3062 return this->assembleOutAssignedIntrinsic(
"frexp",
"fract",
"exp", call);
3064 case k_greaterThan_IntrinsicKind:
3065 return this->assembleBinaryOpIntrinsic(
OperatorKind::GT, call, parentPrecedence);
3067 case k_greaterThanEqual_IntrinsicKind:
3070 case k_inverse_IntrinsicKind:
3071 return this->assembleInversePolyfill(call);
3073 case k_inversesqrt_IntrinsicKind:
3074 return this->assembleSimpleIntrinsic(
"inverseSqrt", call);
3076 case k_lessThan_IntrinsicKind:
3077 return this->assembleBinaryOpIntrinsic(
OperatorKind::LT, call, parentPrecedence);
3079 case k_lessThanEqual_IntrinsicKind:
3082 case k_matrixCompMult_IntrinsicKind: {
3085 ? this->writeScratchLet(*arguments[0], Precedence::kPostfix)
3086 : this->writeNontrivialScratchLet(*arguments[0], Precedence::
kPostfix);
3087 std::string arg1 = this->writeNontrivialScratchLet(*arguments[1], Precedence::kPostfix);
3088 return this->writeScratchLet(
3089 this->assembleComponentwiseMatrixBinary(arguments[0]->
type(),
3090 arguments[1]->
type(),
3095 case k_mix_IntrinsicKind: {
3096 const char*
name = arguments[2]->type().componentType().isBoolean() ?
"select" :
"mix";
3097 return this->assembleVectorizedIntrinsic(
name, call);
3099 case k_mod_IntrinsicKind: {
3104 ? this->writeScratchLet(*arguments[0], Precedence::kAdditive)
3105 : this->writeNontrivialScratchLet(*arguments[0], Precedence::
kAdditive);
3106 std::string arg1 = this->writeNontrivialScratchLet(*arguments[1],
3107 Precedence::kAdditive);
3108 return this->writeScratchLet(arg0 +
" - " + arg1 +
" * floor(" +
3109 arg0 +
" / " + arg1 +
")");
3112 case k_modf_IntrinsicKind:
3115 return this->assembleOutAssignedIntrinsic(
"modf",
"fract",
"whole", call);
3117 case k_normalize_IntrinsicKind: {
3118 const char*
name = arguments[0]->type().isScalar() ?
"sign" :
"normalize";
3119 return this->assembleSimpleIntrinsic(
name, call);
3121 case k_not_IntrinsicKind:
3124 case k_notEqual_IntrinsicKind:
3125 return this->assembleBinaryOpIntrinsic(
OperatorKind::NEQ, call, parentPrecedence);
3127 case k_packHalf2x16_IntrinsicKind:
3128 return this->assembleSimpleIntrinsic(
"pack2x16float", call);
3130 case k_packSnorm2x16_IntrinsicKind:
3131 return this->assembleSimpleIntrinsic(
"pack2x16snorm", call);
3133 case k_packSnorm4x8_IntrinsicKind:
3134 return this->assembleSimpleIntrinsic(
"pack4x8snorm", call);
3136 case k_packUnorm2x16_IntrinsicKind:
3137 return this->assembleSimpleIntrinsic(
"pack2x16unorm", call);
3139 case k_packUnorm4x8_IntrinsicKind:
3140 return this->assembleSimpleIntrinsic(
"pack4x8unorm", call);
3142 case k_reflect_IntrinsicKind:
3143 if (arguments[0]->
type().isScalar()) {
3146 std::string
I = this->writeNontrivialScratchLet(*arguments[0],
3147 Precedence::kAdditive);
3149 ? this->writeScratchLet(*arguments[1], Precedence::kMultiplicative)
3150 : this->writeNontrivialScratchLet(*arguments[1], Precedence::
kMultiplicative);
3151 return this->writeScratchLet(
String::printf(
"%s - 2 * %s * %s * %s",
3152 I.c_str(),
N.c_str(),
3153 I.c_str(),
N.c_str()));
3155 return this->assembleSimpleIntrinsic(
"reflect", call);
3157 case k_refract_IntrinsicKind:
3158 if (arguments[0]->
type().isScalar()) {
3161 std::string
I = this->writeNontrivialScratchLet(*arguments[0],
3162 Precedence::kSequence);
3163 std::string
N = this->writeNontrivialScratchLet(*arguments[1],
3164 Precedence::kSequence);
3167 ? this->writeScratchLet(*arguments[2], Precedence::kSequence)
3168 : this->writeNontrivialScratchLet(*arguments[2], Precedence::
kSequence);
3169 return this->writeScratchLet(
3170 String::printf(
"refract(vec2<%s>(%s, 0), vec2<%s>(%s, 0), %s).x",
3177 return this->assembleSimpleIntrinsic(
"refract", call);
3179 case k_sample_IntrinsicKind: {
3181 SkASSERT(arguments.size() == 2 || arguments.size() == 3);
3182 bool callIncludesBias = (arguments.size() == 3);
3184 if (
fProgram.
fConfig->fSettings.fSharpenTextures || callIncludesBias) {
3186 std::string expr = this->assemblePartialSampleCall(
"textureSampleBias",
3190 if (callIncludesBias) {
3191 expr += this->assembleExpression(*arguments[2], Precedence::kAdditive) +
3201 return this->assemblePartialSampleCall(
"textureSample",
3203 *arguments[1]) +
')';
3205 case k_sampleLod_IntrinsicKind: {
3206 std::string expr = this->assemblePartialSampleCall(
"textureSampleLevel",
3209 expr +=
", " + this->assembleExpression(*arguments[2], Precedence::kSequence);
3212 case k_sampleGrad_IntrinsicKind: {
3213 std::string expr = this->assemblePartialSampleCall(
"textureSampleGrad",
3216 expr +=
", " + this->assembleExpression(*arguments[2], Precedence::kSequence);
3217 expr +=
", " + this->assembleExpression(*arguments[3], Precedence::kSequence);
3220 case k_textureHeight_IntrinsicKind:
3221 return this->assembleSimpleIntrinsic(
"textureDimensions", call) +
".y";
3223 case k_textureRead_IntrinsicKind: {
3226 std::string tex = this->assembleExpression(*arguments[0], Precedence::kSequence);
3227 std::string
pos = this->writeScratchLet(*arguments[1], Precedence::kSequence);
3228 return std::string(
"textureLoad(") + tex +
", " +
pos +
", 0)";
3230 case k_textureWidth_IntrinsicKind:
3231 return this->assembleSimpleIntrinsic(
"textureDimensions", call) +
".x";
3233 case k_textureWrite_IntrinsicKind:
3234 return this->assembleSimpleIntrinsic(
"textureStore", call);
3236 case k_unpackHalf2x16_IntrinsicKind:
3237 return this->assembleSimpleIntrinsic(
"unpack2x16float", call);
3239 case k_unpackSnorm2x16_IntrinsicKind:
3240 return this->assembleSimpleIntrinsic(
"unpack2x16snorm", call);
3242 case k_unpackSnorm4x8_IntrinsicKind:
3243 return this->assembleSimpleIntrinsic(
"unpack4x8snorm", call);
3245 case k_unpackUnorm2x16_IntrinsicKind:
3246 return this->assembleSimpleIntrinsic(
"unpack2x16unorm", call);
3248 case k_unpackUnorm4x8_IntrinsicKind:
3249 return this->assembleSimpleIntrinsic(
"unpack4x8unorm", call);
3251 case k_clamp_IntrinsicKind:
3252 case k_max_IntrinsicKind:
3253 case k_min_IntrinsicKind:
3254 case k_smoothstep_IntrinsicKind:
3255 case k_step_IntrinsicKind:
3256 return this->assembleVectorizedIntrinsic(
call.function().name(), call);
3258 case k_abs_IntrinsicKind:
3259 case k_acos_IntrinsicKind:
3260 case k_all_IntrinsicKind:
3261 case k_any_IntrinsicKind:
3262 case k_asin_IntrinsicKind:
3263 case k_atomicAdd_IntrinsicKind:
3264 case k_atomicLoad_IntrinsicKind:
3265 case k_atomicStore_IntrinsicKind:
3266 case k_ceil_IntrinsicKind:
3267 case k_cos_IntrinsicKind:
3268 case k_cross_IntrinsicKind:
3269 case k_degrees_IntrinsicKind:
3270 case k_distance_IntrinsicKind:
3271 case k_exp_IntrinsicKind:
3272 case k_exp2_IntrinsicKind:
3273 case k_floor_IntrinsicKind:
3274 case k_fract_IntrinsicKind:
3275 case k_length_IntrinsicKind:
3276 case k_log_IntrinsicKind:
3277 case k_log2_IntrinsicKind:
3278 case k_radians_IntrinsicKind:
3279 case k_pow_IntrinsicKind:
3280 case k_saturate_IntrinsicKind:
3281 case k_sign_IntrinsicKind:
3282 case k_sin_IntrinsicKind:
3283 case k_sqrt_IntrinsicKind:
3284 case k_storageBarrier_IntrinsicKind:
3285 case k_tan_IntrinsicKind:
3286 case k_workgroupBarrier_IntrinsicKind:
3288 return this->assembleSimpleIntrinsic(
call.function().name(), call);
3293 "fn mat2_inverse(m: mat2x2<f32>) -> mat2x2<f32> {"
3294"\n" "return mat2x2<f32>(m[1].y, -m[0].y, -m[1].x, m[0].x) * (1/determinant(m));"
3299 "fn mat3_inverse(m: mat3x3<f32>) -> mat3x3<f32> {"
3300"\n" "let a00 = m[0].x; let a01 = m[0].y; let a02 = m[0].z;"
3301"\n" "let a10 = m[1].x; let a11 = m[1].y; let a12 = m[1].z;"
3302"\n" "let a20 = m[2].x; let a21 = m[2].y; let a22 = m[2].z;"
3303"\n" "let b01 = a22*a11 - a12*a21;"
3304"\n" "let b11 = -a22*a10 + a12*a20;"
3305"\n" "let b21 = a21*a10 - a11*a20;"
3306"\n" "let det = a00*b01 + a01*b11 + a02*b21;"
3307"\n" "return mat3x3<f32>(b01, (-a22*a01 + a02*a21), ( a12*a01 - a02*a11),"
3308"\n" "b11, ( a22*a00 - a02*a20), (-a12*a00 + a02*a10),"
3309"\n" "b21, (-a21*a00 + a01*a20), ( a11*a00 - a01*a10)) * (1/det);"
3314 "fn mat4_inverse(m: mat4x4<f32>) -> mat4x4<f32>{"
3315"\n" "let a00 = m[0].x; let a01 = m[0].y; let a02 = m[0].z; let a03 = m[0].w;"
3316"\n" "let a10 = m[1].x; let a11 = m[1].y; let a12 = m[1].z; let a13 = m[1].w;"
3317"\n" "let a20 = m[2].x; let a21 = m[2].y; let a22 = m[2].z; let a23 = m[2].w;"
3318"\n" "let a30 = m[3].x; let a31 = m[3].y; let a32 = m[3].z; let a33 = m[3].w;"
3319"\n" "let b00 = a00*a11 - a01*a10;"
3320"\n" "let b01 = a00*a12 - a02*a10;"
3321"\n" "let b02 = a00*a13 - a03*a10;"
3322"\n" "let b03 = a01*a12 - a02*a11;"
3323"\n" "let b04 = a01*a13 - a03*a11;"
3324"\n" "let b05 = a02*a13 - a03*a12;"
3325"\n" "let b06 = a20*a31 - a21*a30;"
3326"\n" "let b07 = a20*a32 - a22*a30;"
3327"\n" "let b08 = a20*a33 - a23*a30;"
3328"\n" "let b09 = a21*a32 - a22*a31;"
3329"\n" "let b10 = a21*a33 - a23*a31;"
3330"\n" "let b11 = a22*a33 - a23*a32;"
3331"\n" "let det = b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06;"
3332"\n" "return mat4x4<f32>(a11*b11 - a12*b10 + a13*b09,"
3333"\n" "a02*b10 - a01*b11 - a03*b09,"
3334"\n" "a31*b05 - a32*b04 + a33*b03,"
3335"\n" "a22*b04 - a21*b05 - a23*b03,"
3336"\n" "a12*b08 - a10*b11 - a13*b07,"
3337"\n" "a00*b11 - a02*b08 + a03*b07,"
3338"\n" "a32*b02 - a30*b05 - a33*b01,"
3339"\n" "a20*b05 - a22*b02 + a23*b01,"
3340"\n" "a10*b10 - a11*b08 + a13*b06,"
3341"\n" "a01*b08 - a00*b10 - a03*b06,"
3342"\n" "a30*b04 - a31*b02 + a33*b00,"
3343"\n" "a21*b02 - a20*b04 - a23*b00,"
3344"\n" "a11*b07 - a10*b09 - a12*b06,"
3345"\n" "a00*b09 - a01*b07 + a02*b06,"
3346"\n" "a31*b01 - a30*b03 - a32*b00,"
3347"\n" "a20*b03 - a21*b01 + a22*b00) * (1/det);"
3351std::string WGSLCodeGenerator::assembleInversePolyfill(
const FunctionCall& call) {
3362 switch (
type.slotCount()) {
3364 if (!fWrittenInverse2) {
3365 fWrittenInverse2 =
true;
3368 return this->assembleSimpleIntrinsic(
"mat2_inverse", call);
3371 if (!fWrittenInverse3) {
3372 fWrittenInverse3 =
true;
3375 return this->assembleSimpleIntrinsic(
"mat3_inverse", call);
3378 if (!fWrittenInverse4) {
3379 fWrittenInverse4 =
true;
3382 return this->assembleSimpleIntrinsic(
"mat4_inverse", call);
3390std::string WGSLCodeGenerator::assembleFunctionCall(
const FunctionCall& call,
3391 Precedence parentPrecedence) {
3392 const FunctionDeclaration& func = call.function();
3396 if (func.isIntrinsic()) {
3397 return this->assembleIntrinsicCall(call, func.intrinsicKind(), parentPrecedence);
3413 const ExpressionArray&
args =
call.arguments();
3422 for (
int index = 0; index <
args.size(); ++index) {
3424 std::unique_ptr<LValue> lvalue = this->makeLValue(*
args[index]);
3435 substituteArgument.
push_back(std::string());
3440 std::string expr = this->assembleName(func.mangledName());
3441 expr.push_back(
'(');
3444 if (std::string funcDepArgs = this->functionDependencyArgs(func); !funcDepArgs.empty()) {
3445 expr += funcDepArgs;
3450 for (
int index = 0; index <
args.size(); ++index) {
3451 expr += separator();
3452 if (!substituteArgument[index].
empty()) {
3454 expr +=
'&' + substituteArgument[index];
3455 }
else if (
args[index]->
type().isSampler()) {
3459 expr += this->assembleExpression(*
args[index], Precedence::kSequence);
3460 expr += kTextureSuffix;
3462 expr += this->assembleExpression(*
args[index], Precedence::kSequence);
3463 expr += kSamplerSuffix;
3465 expr += this->assembleExpression(*
args[index], Precedence::kSequence);
3470 if (
call.type().isVoid()) {
3474 SkASSERT(parentPrecedence >= Precedence::kSequence);
3476 this->writeLine(
";");
3478 result = this->writeScratchLet(expr);
3482 for (
int index = 0; index <
args.size(); ++index) {
3483 if (!substituteArgument[index].
empty()) {
3484 this->writeLine(writeback[index]->
store(substituteArgument[index]));
3492std::string WGSLCodeGenerator::assembleIndexExpression(
const IndexExpression& i) {
3494 std::string idx = this->writeNontrivialScratchLet(*i.index(), Precedence::kExpression);
3495 return this->assembleExpression(*i.base(), Precedence::kPostfix) +
"[" + idx +
"]";
3498std::string WGSLCodeGenerator::assembleLiteral(
const Literal& l) {
3500 if (
type.isFloat() ||
type.isBoolean()) {
3505 return std::to_string(l.intValue() & 0xffffffff) +
"u";
3507 return std::to_string(l.intValue() & 0xffff) +
"u";
3509 return std::to_string(l.intValue());
3513std::string WGSLCodeGenerator::assembleIncrementExpr(
const Type&
type) {
3516 expr.push_back(
'(');
3520 for (
int slots =
type.slotCount(); slots > 0; --slots) {
3521 expr += separator();
3524 expr.push_back(
')');
3528std::string WGSLCodeGenerator::assemblePrefixExpression(
const PrefixExpression& p,
3529 Precedence parentPrecedence) {
3531 Operator op =
p.getOperator();
3532 if (op.kind() == Operator::Kind::PLUS) {
3533 return this->assembleExpression(*
p.operand(), Precedence::kPrefix);
3537 if (op.kind() == Operator::Kind::PLUSPLUS || op.kind() == Operator::Kind::MINUSMINUS) {
3538 std::unique_ptr<LValue> lvalue = this->makeLValue(*
p.operand());
3544 std::string newValue =
3546 (
p.getOperator().kind() == Operator::Kind::PLUSPLUS ?
" + " :
" - ") +
3547 this->assembleIncrementExpr(
p.operand()->type());
3548 this->writeLine(lvalue->store(newValue));
3549 return lvalue->load();
3560 const bool needsNegation = op.kind() == Operator::Kind::MINUS &&
3561 !
p.operand()->type().isScalar() && !
p.operand()->type().isVector();
3562 const bool needParens = needsNegation || Precedence::kPrefix >= parentPrecedence;
3565 expr.push_back(
'(');
3568 if (needsNegation) {
3570 expr += this->assembleExpression(*
p.operand(), Precedence::kMultiplicative);
3572 expr +=
p.getOperator().tightOperatorName();
3573 expr += this->assembleExpression(*
p.operand(), Precedence::kPrefix);
3577 expr.push_back(
')');
3583std::string WGSLCodeGenerator::assemblePostfixExpression(
const PostfixExpression& p,
3584 Precedence parentPrecedence) {
3585 SkASSERT(
p.getOperator().kind() == Operator::Kind::PLUSPLUS ||
3586 p.getOperator().kind() == Operator::Kind::MINUSMINUS);
3590 std::unique_ptr<LValue> lvalue = this->makeLValue(*
p.operand());
3597 std::string originalValue;
3598 if (parentPrecedence != Precedence::kStatement) {
3599 originalValue = this->writeScratchLet(lvalue->load());
3602 std::string newValue = lvalue->load() +
3603 (
p.getOperator().kind() == Operator::Kind::PLUSPLUS ?
" + " :
" - ") +
3604 this->assembleIncrementExpr(
p.operand()->type());
3605 this->writeLine(lvalue->store(newValue));
3607 return originalValue;
3610std::string WGSLCodeGenerator::assembleSwizzle(
const Swizzle& swizzle) {
3611 return this->assembleExpression(*swizzle.base(), Precedence::kPostfix) +
"." +
3615std::string WGSLCodeGenerator::writeScratchVar(
const Type&
type,
const std::string& value) {
3616 std::string scratchVarName =
"_skTemp" + std::to_string(fScratchCount++);
3617 this->write(
"var ");
3618 this->write(scratchVarName);
3621 if (!
value.empty()) {
3625 this->writeLine(
";");
3626 return scratchVarName;
3629std::string WGSLCodeGenerator::writeScratchLet(
const std::string& expr) {
3630 std::string scratchVarName =
"_skTemp" + std::to_string(fScratchCount++);
3631 this->write(fAtFunctionScope ?
"let " :
"const ");
3632 this->write(scratchVarName);
3635 this->writeLine(
";");
3636 return scratchVarName;
3639std::string WGSLCodeGenerator::writeScratchLet(
const Expression& expr,
3640 Precedence parentPrecedence) {
3641 return this->writeScratchLet(this->assembleExpression(expr, parentPrecedence));
3644std::string WGSLCodeGenerator::writeNontrivialScratchLet(
const Expression& expr,
3645 Precedence parentPrecedence) {
3646 std::string
result = this->assembleExpression(expr, parentPrecedence);
3651std::string WGSLCodeGenerator::assembleTernaryExpression(
const TernaryExpression& t,
3652 Precedence parentPrecedence) {
3659 if ((t.type().isScalar() || t.type().isVector()) &&
3664 bool needParens = Precedence::kTernary >= parentPrecedence;
3666 expr.push_back(
'(');
3669 expr += this->assembleExpression(*t.ifFalse(), Precedence::kSequence);
3671 expr += this->assembleExpression(*t.ifTrue(), Precedence::kSequence);
3674 bool isVector = t.type().isVector();
3679 expr += this->assembleExpression(*t.test(), Precedence::kSequence);
3681 expr.push_back(
')');
3683 expr.push_back(
')');
3685 expr.push_back(
')');
3691 expr = this->writeScratchVar(t.ifTrue()->type());
3693 std::string testExpr = this->assembleExpression(*t.test(), Precedence::kExpression);
3695 this->write(testExpr);
3696 this->writeLine(
" {");
3699 std::string trueExpr = this->assembleExpression(*t.ifTrue(), Precedence::kAssignment);
3702 this->write(trueExpr);
3703 this->writeLine(
";");
3706 this->writeLine(
"} else {");
3709 std::string falseExpr = this->assembleExpression(*t.ifFalse(), Precedence::kAssignment);
3712 this->write(falseExpr);
3713 this->writeLine(
";");
3716 this->writeLine(
"}");
3721std::string WGSLCodeGenerator::variablePrefix(
const Variable& v) {
3722 if (v.storage() == Variable::Storage::kGlobal) {
3731 return "(*_stageOut).";
3736 if (
const InterfaceBlock* ib = v.interfaceBlock()) {
3737 const Type& ibType = ib->var()->type().componentType();
3738 if (
const std::string* ibName = fInterfaceBlockNameMap.find(&ibType)) {
3739 return *ibName +
'.';
3746 if (is_in_global_uniforms(v)) {
3747 return "_globalUniforms.";
3754std::string WGSLCodeGenerator::variableReferenceNameForLValue(
const VariableReference& r) {
3755 const Variable& v = *r.variable();
3757 if ((v.storage() == Variable::Storage::kParameter &&
3761 return "(*" + this->assembleName(v.mangledName()) +
')';
3764 return this->variablePrefix(v) + this->assembleName(v.mangledName());
3767std::string WGSLCodeGenerator::assembleVariableReference(
const VariableReference& r) {
3769 const Variable& v = *r.variable();
3774 std::optional<std::string_view> conversion = needs_builtin_type_conversion(v);
3775 if (conversion.has_value()) {
3776 expr += *conversion;
3777 expr.push_back(
'(');
3780 expr += this->variableReferenceNameForLValue(r);
3782 if (conversion.has_value()) {
3783 expr.push_back(
')');
3789std::string WGSLCodeGenerator::assembleAnyConstructor(
const AnyConstructor& c) {
3790 std::string expr = to_wgsl_type(
fContext, c.type());
3791 expr.push_back(
'(');
3793 for (
const auto& e : c.argumentSpan()) {
3794 expr += separator();
3795 expr += this->assembleExpression(*e, Precedence::kSequence);
3797 expr.push_back(
')');
3801std::string WGSLCodeGenerator::assembleConstructorCompound(
const ConstructorCompound& c) {
3802 if (c.type().isVector()) {
3803 return this->assembleConstructorCompoundVector(c);
3804 }
else if (c.type().isMatrix()) {
3805 return this->assembleConstructorCompoundMatrix(c);
3812std::string WGSLCodeGenerator::assembleConstructorCompoundVector(
const ConstructorCompound& c) {
3817 if (c.type().columns() == 4 && c.argumentSpan().size() == 1) {
3818 const Expression& arg = *c.argumentSpan().front();
3819 if (arg.type().isMatrix()) {
3820 SkASSERT(arg.type().columns() == 2);
3823 std::string
matrix = this->writeNontrivialScratchLet(arg, Precedence::kPostfix);
3829 return this->assembleAnyConstructor(c);
3832std::string WGSLCodeGenerator::assembleConstructorCompoundMatrix(
const ConstructorCompound& ctor) {
3835 std::string expr = to_wgsl_type(
fContext, ctor.type()) +
'(';
3837 for (
const std::unique_ptr<Expression>& arg : ctor.arguments()) {
3838 SkASSERT(arg->type().isScalar() || arg->type().isVector());
3840 if (arg->type().isScalar()) {
3841 expr += separator();
3842 expr += this->assembleExpression(*arg, Precedence::kSequence);
3844 std::string inner = this->writeNontrivialScratchLet(*arg, Precedence::kSequence);
3845 int numSlots = arg->type().slotCount();
3846 for (
int slot = 0; slot < numSlots; ++slot) {
3847 String::appendf(&expr,
"%s%s[%d]", separator().c_str(), inner.c_str(), slot);
3854std::string WGSLCodeGenerator::assembleConstructorDiagonalMatrix(
3855 const ConstructorDiagonalMatrix& c) {
3858 SkASSERT(c.argument()->type().isScalar());
3861 std::string inner = this->writeNontrivialScratchLet(*c.argument(), Precedence::kAssignment);
3866 for (
int col = 0; col <
type.columns(); ++col) {
3867 for (
int row = 0; row <
type.rows(); ++row) {
3868 expr += separator();
3879std::string WGSLCodeGenerator::assembleConstructorMatrixResize(
3880 const ConstructorMatrixResize& ctor) {
3881 std::string
source = this->writeScratchLet(this->assembleExpression(*ctor.argument(),
3882 Precedence::kSequence));
3883 int columns = ctor.type().columns();
3884 int rows = ctor.type().rows();
3885 int sourceColumns = ctor.argument()->type().columns();
3886 int sourceRows = ctor.argument()->type().rows();
3888 std::string expr = to_wgsl_type(
fContext, ctor.type()) +
'(';
3890 for (
int c = 0; c < columns; ++c) {
3891 for (
int r = 0; r < rows; ++r) {
3892 expr += separator();
3893 if (c < sourceColumns && r < sourceRows) {
3895 }
else if (r == c) {
3906std::string WGSLCodeGenerator::assembleEqualityExpression(
const Type&
left,
3907 const std::string& leftName,
3909 const std::string& rightName,
3911 Precedence parentPrecedence) {
3915 bool isEqual = (op.kind() == Operator::Kind::EQEQ);
3916 const char*
const combiner = isEqual ?
" && " :
" || ";
3918 if (
left.isMatrix()) {
3923 int columns =
left.columns();
3925 const char* separator =
"(";
3926 for (
int index = 0; index < columns; ++index) {
3928 std::string
suffix =
'[' + std::to_string(index) +
']';
3929 expr += this->assembleEqualityExpression(vecType, leftName + suffix,
3930 vecType, rightName + suffix,
3931 op, Precedence::kParentheses);
3932 separator = combiner;
3937 if (
left.isArray()) {
3939 const Type& indexedType =
left.componentType();
3940 const char* separator =
"(";
3941 for (
int index = 0; index <
left.columns(); ++index) {
3943 std::string
suffix =
'[' + std::to_string(index) +
']';
3944 expr += this->assembleEqualityExpression(indexedType, leftName + suffix,
3945 indexedType, rightName + suffix,
3946 op, Precedence::kParentheses);
3947 separator = combiner;
3952 if (
left.isStruct()) {
3957 const char* separator =
"(";
3958 for (
const Field& field : fields) {
3960 expr += this->assembleEqualityExpression(
3961 *field.fType, leftName +
'.' + this->assembleName(field.fName),
3962 *field.fType, rightName +
'.' + this->assembleName(field.fName),
3963 op, Precedence::kParentheses);
3964 separator = combiner;
3969 if (
left.isVector()) {
3974 expr += isEqual ?
"all(" :
"any(";
3983 if (parentPrecedence < Precedence::kSequence) {
3989 if (parentPrecedence < Precedence::kSequence) {
3995std::string WGSLCodeGenerator::assembleEqualityExpression(
const Expression&
left,
3996 const Expression&
right,
3998 Precedence parentPrecedence) {
3999 std::string leftName, rightName;
4000 if (
left.type().isScalar() ||
left.type().isVector()) {
4003 leftName = this->assembleExpression(
left, Precedence::kParentheses);
4004 rightName = this->assembleExpression(
right, Precedence::kParentheses);
4006 leftName = this->writeNontrivialScratchLet(
left, Precedence::kAssignment);
4007 rightName = this->writeNontrivialScratchLet(
right, Precedence::kAssignment);
4009 return this->assembleEqualityExpression(
left.type(), leftName,
right.type(), rightName,
4010 op, parentPrecedence);
4013void WGSLCodeGenerator::writeProgramElement(
const ProgramElement& e) {
4015 case ProgramElement::Kind::kExtension:
4021 case ProgramElement::Kind::kGlobalVar:
4022 this->writeGlobalVarDeclaration(
e.as<GlobalVarDeclaration>());
4024 case ProgramElement::Kind::kInterfaceBlock:
4028 case ProgramElement::Kind::kStructDefinition:
4029 this->writeStructDefinition(
e.as<StructDefinition>());
4031 case ProgramElement::Kind::kFunctionPrototype:
4038 case ProgramElement::Kind::kFunction:
4039 this->writeFunction(
e.as<FunctionDefinition>());
4041 case ProgramElement::Kind::kModifiers:
4042 this->writeModifiersDeclaration(
e.as<ModifiersDeclaration>());
4045 SkDEBUGFAILF(
"unsupported program element: %s\n",
e.description().c_str());
4050void WGSLCodeGenerator::writeTextureOrSampler(
const Variable& var,
4051 int bindingLocation,
4052 std::string_view suffix,
4053 std::string_view wgslType) {
4054 if (var.type().dimensions() !=
SpvDim2D) {
4060 this->write(
"@group(");
4061 this->write(std::to_string(std::max(0, var.layout().fSet)));
4062 this->write(
") @binding(");
4063 this->write(std::to_string(bindingLocation));
4064 this->write(
") var ");
4065 this->write(this->assembleName(var.mangledName()));
4066 this->write(suffix);
4068 this->write(wgslType);
4069 this->writeLine(
";");
4072void WGSLCodeGenerator::writeGlobalVarDeclaration(
const GlobalVarDeclaration&
d) {
4073 const VarDeclaration& decl =
d.varDeclaration();
4074 const Variable& var = *decl.var();
4076 is_in_global_uniforms(var)) {
4086 int samplerLocation = var.layout().fSampler >= 0 ? var.layout().fSampler
4087 : 10000 + fScratchCount++;
4088 this->writeTextureOrSampler(var, samplerLocation, kSamplerSuffix,
"sampler");
4091 int textureLocation = var.layout().fTexture >= 0 ? var.layout().fTexture
4092 : 10000 + fScratchCount++;
4093 this->writeTextureOrSampler(var, textureLocation, kTextureSuffix,
"texture_2d<f32>");
4099 int textureLocation = var.layout().fBinding >= 0 ? var.layout().fBinding
4100 : 10000 + fScratchCount++;
4102 this->writeTextureOrSampler(var, textureLocation,
"",
4103 to_wgsl_type(
fContext, var.type(), &var.layout()));
4113 initializer += this->assembleExpression(*decl.value(), Precedence::kAssignment);
4116 if (var.modifierFlags().isConst()) {
4117 this->write(
"const ");
4118 }
else if (var.modifierFlags().isWorkgroup()) {
4119 this->write(
"var<workgroup> ");
4120 }
else if (var.modifierFlags().isPixelLocal()) {
4121 this->write(
"var<pixel_local> ");
4123 this->write(
"var<private> ");
4125 this->write(this->assembleName(var.mangledName()));
4126 this->write(
": " + to_wgsl_type(
fContext, var.type(), &var.layout()));
4128 this->writeLine(
";");
4131void WGSLCodeGenerator::writeStructDefinition(
const StructDefinition&
s) {
4133 this->writeLine(
"struct " +
type.displayName() +
" {");
4134 this->writeFields(
type.fields(),
nullptr);
4135 this->writeLine(
"};");
4138void WGSLCodeGenerator::writeModifiersDeclaration(
const ModifiersDeclaration& modifiers) {
4146 if (modifiers.layout().fLocalSizeX >= 0) {
4147 fLocalSizeX = modifiers.layout().fLocalSizeX;
4149 if (modifiers.layout().fLocalSizeY >= 0) {
4150 fLocalSizeY = modifiers.layout().fLocalSizeY;
4152 if (modifiers.layout().fLocalSizeZ >= 0) {
4153 fLocalSizeZ = modifiers.layout().fLocalSizeZ;
4157void WGSLCodeGenerator::writeFields(
SkSpan<const Field> fields,
const MemoryLayout* memoryLayout) {
4162 for (
size_t index = 0; index < fields.
size(); ++index) {
4163 const Field& field = fields[index];
4164 if (memoryLayout && !memoryLayout->isSupported(*field.fType)) {
4167 "' is not permitted here");
4173 if (index < fields.
size() - 1) {
4174 int thisFieldOffset = field.fLayout.fOffset;
4175 int nextFieldOffset = fields[index + 1].fLayout.fOffset;
4176 if (index == 0 && thisFieldOffset > 0) {
4180 if (thisFieldOffset >= 0 && nextFieldOffset > thisFieldOffset) {
4181 this->write(
"@size(");
4182 this->write(std::to_string(nextFieldOffset - thisFieldOffset));
4187 this->write(this->assembleName(field.fName));
4189 if (
const FieldPolyfillInfo*
info = fFieldPolyfillMap.
find(&field)) {
4190 if (
info->fIsArray) {
4193 this->write(
"array<_skArrayElement_");
4194 this->write(field.fType->abbreviatedName());
4196 this->write(std::to_string(field.fType->columns()));
4198 }
else if (
info->fIsMatrix) {
4199 this->write(
"_skMatrix");
4200 this->write(std::to_string(field.fType->columns()));
4201 this->write(std::to_string(field.fType->rows()));
4206 this->write(to_wgsl_type(
fContext, *field.fType, &field.fLayout));
4208 this->writeLine(
",");
4214void WGSLCodeGenerator::writeEnables() {
4215 this->writeLine(
"diagnostic(off, derivative_uniformity);");
4216 this->writeLine(
"diagnostic(off, chromium.unreachable_code);");
4219 this->writeLine(
"enable chromium_experimental_pixel_local;");
4222 this->writeLine(
"enable chromium_experimental_framebuffer_fetch;");
4225 this->writeLine(
"enable chromium_internal_dual_source_blending;");
4229bool WGSLCodeGenerator::needsStageInputStruct()
const {
4232 return !fPipelineInputs.empty();
4235void WGSLCodeGenerator::writeStageInputStruct() {
4236 if (!this->needsStageInputStruct()) {
4240 std::string_view structNamePrefix = pipeline_struct_prefix(
fProgram.
fConfig->fKind);
4241 SkASSERT(!structNamePrefix.empty());
4243 this->write(
"struct ");
4244 this->write(structNamePrefix);
4245 this->writeLine(
"In {");
4248 for (
const Variable* v : fPipelineInputs) {
4249 if (v->type().isInterfaceBlock()) {
4250 for (
const Field& f : v->
type().fields()) {
4254 this->writePipelineIODeclaration(v->layout(), v->type(), v->mangledName(),
4260 this->writeLine(
"};");
4263bool WGSLCodeGenerator::needsStageOutputStruct()
const {
4270void WGSLCodeGenerator::writeStageOutputStruct() {
4271 if (!this->needsStageOutputStruct()) {
4275 std::string_view structNamePrefix = pipeline_struct_prefix(
fProgram.
fConfig->fKind);
4276 SkASSERT(!structNamePrefix.empty());
4278 this->write(
"struct ");
4279 this->write(structNamePrefix);
4280 this->writeLine(
"Out {");
4283 bool declaredPositionBuiltin =
false;
4284 bool requiresPointSizeBuiltin =
false;
4285 for (
const Variable* v : fPipelineOutputs) {
4286 if (v->type().isInterfaceBlock()) {
4287 for (
const auto& f : v->
type().fields()) {
4290 declaredPositionBuiltin =
true;
4295 requiresPointSizeBuiltin =
true;
4299 this->writePipelineIODeclaration(v->layout(), v->type(), v->mangledName(),
4306 if (positionBuiltinRequired && !declaredPositionBuiltin) {
4307 this->writeLine(
"@builtin(position) sk_Position: vec4<f32>,");
4311 this->writeLine(
"};");
4320 this->writeLine(
"/* unsupported */ var<private> sk_PointSize: f32;");
4324void WGSLCodeGenerator::prepareUniformPolyfillsForInterfaceBlock(
4325 const InterfaceBlock* interfaceBlock,
4326 std::string_view instanceName,
4331 const Type& structType = interfaceBlock->var()->type().componentType();
4332 for (
const Field& field : structType.fields()) {
4334 bool needsArrayPolyfill =
false;
4335 bool needsMatrixPolyfill =
false;
4337 auto isPolyfillableMatrixType = [&](
const Type*
type) {
4338 return type->isMatrix() && std140.stride(*
type) != native.stride(*
type);
4341 if (isPolyfillableMatrixType(
type)) {
4344 needsMatrixPolyfill =
true;
4345 }
else if (
type->isArray() && !
type->isUnsizedArray() &&
4346 !
type->componentType().isOpaque()) {
4347 const Type* innerType = &
type->componentType();
4348 if (isPolyfillableMatrixType(innerType)) {
4350 needsArrayPolyfill =
true;
4351 needsMatrixPolyfill =
true;
4352 }
else if (native.size(*innerType) < 16) {
4355 needsArrayPolyfill =
true;
4359 if (needsArrayPolyfill || needsMatrixPolyfill) {
4361 FieldPolyfillInfo
info;
4362 info.fInterfaceBlock = interfaceBlock;
4363 info.fReplacementName =
"_skUnpacked_" + std::string(instanceName) +
'_' +
4364 this->assembleName(field.fName);
4365 info.fIsArray = needsArrayPolyfill;
4366 info.fIsMatrix = needsMatrixPolyfill;
4367 fFieldPolyfillMap.
set(&field,
info);
4372void WGSLCodeGenerator::writeUniformsAndBuffers() {
4373 for (
const ProgramElement* e :
fProgram.elements()) {
4375 if (!
e->is<InterfaceBlock>()) {
4378 const InterfaceBlock& ib =
e->as<InterfaceBlock>();
4381 std::string_view addressSpace;
4382 std::string_view accessMode;
4384 if (ib.var()->modifierFlags().isUniform()) {
4385 addressSpace =
"uniform";
4387 }
else if (ib.var()->modifierFlags().isBuffer()) {
4388 addressSpace =
"storage";
4390 accessMode = ib.var()->modifierFlags().isReadOnly() ?
", read" :
", read_write";
4396 std::string instanceName;
4397 if (ib.instanceName().empty()) {
4398 instanceName =
"_" + std::string(addressSpace) + std::to_string(fScratchCount++);
4399 fInterfaceBlockNameMap[&ib.var()->type().componentType()] = instanceName;
4401 instanceName = std::string(ib.instanceName());
4404 this->prepareUniformPolyfillsForInterfaceBlock(&ib, instanceName, nativeLayout);
4408 this->write(
"struct ");
4409 this->write(ib.typeName());
4410 this->writeLine(
" {");
4413 const Type& ibType = ib.var()->type().componentType();
4420 this->writeFields(ibFields, &layout);
4421 this->writeLine(
"};");
4422 this->write(
"@group(");
4423 this->write(std::to_string(std::max(0, ib.var()->layout().fSet)));
4424 this->write(
") @binding(");
4425 this->write(std::to_string(std::max(0, ib.var()->layout().fBinding)));
4426 this->write(
") var<");
4427 this->write(addressSpace);
4428 this->write(accessMode);
4430 this->write(instanceName);
4432 this->write(to_wgsl_type(
fContext, ib.var()->type(), &ib.var()->layout()));
4433 this->writeLine(
";");
4437void WGSLCodeGenerator::writeNonBlockUniformsForTests() {
4438 bool declaredUniformsStruct =
false;
4440 for (
const ProgramElement* e :
fProgram.elements()) {
4441 if (
e->is<GlobalVarDeclaration>()) {
4442 const GlobalVarDeclaration& decls =
e->as<GlobalVarDeclaration>();
4443 const Variable& var = *decls.varDeclaration().var();
4444 if (is_in_global_uniforms(var)) {
4445 if (!declaredUniformsStruct) {
4446 this->write(
"struct _GlobalUniforms {\n");
4447 declaredUniformsStruct =
true;
4450 this->writeVariableDecl(var.layout(), var.type(), var.mangledName(),
4455 if (declaredUniformsStruct) {
4458 this->write(
"};\n");
4459 this->write(
"@binding(" + std::to_string(binding) +
") ");
4460 this->write(
"@group(" + std::to_string(set) +
") ");
4461 this->writeLine(
"var<uniform> _globalUniforms: _GlobalUniforms;");
4465std::string WGSLCodeGenerator::functionDependencyArgs(
const FunctionDeclaration& f) {
4468 if (deps && *deps) {
4469 const char* separator =
"";
4470 if (*deps & WGSLFunctionDependency::kPipelineInputs) {
4474 if (*deps & WGSLFunctionDependency::kPipelineOutputs) {
4476 args +=
"_stageOut";
4482bool WGSLCodeGenerator::writeFunctionDependencyParams(
const FunctionDeclaration& f) {
4484 if (!deps || !*deps) {
4488 std::string_view structNamePrefix = pipeline_struct_prefix(
fProgram.
fConfig->fKind);
4489 if (structNamePrefix.empty()) {
4492 const char* separator =
"";
4493 if (*deps & WGSLFunctionDependency::kPipelineInputs) {
4494 this->write(
"_stageIn: ");
4496 this->write(structNamePrefix);
4499 if (*deps & WGSLFunctionDependency::kPipelineOutputs) {
4500 this->write(separator);
4501 this->write(
"_stageOut: ptr<function, ");
4502 this->write(structNamePrefix);
4503 this->write(
"Out>");
4508#if defined(SK_ENABLE_WGSL_VALIDATION)
4509static bool validate_wgsl(ErrorReporter&
reporter,
const std::string& wgsl, std::string* warnings) {
4511 tint::wgsl::reader::Options
options;
4512 for (
auto extension : {tint::wgsl::Extension::kChromiumExperimentalPixelLocal,
4513 tint::wgsl::Extension::kChromiumInternalDualSourceBlending}) {
4514 options.allowed_features.extensions.insert(extension);
4518 tint::Source::File srcFile(
"", wgsl);
4519 tint::Program program(tint::wgsl::reader::Parse(&srcFile,
options));
4521 if (program.Diagnostics().ContainsErrors()) {
4523#if defined(SKSL_STANDALONE)
4524 reporter.error(Position(), std::string(
"Tint compilation failed.\n\n") + wgsl);
4528 tint::diag::Formatter diagFormatter;
4529 std::string diagOutput = diagFormatter.Format(program.Diagnostics()).Plain();
4537 if (!program.Diagnostics().empty()) {
4539 tint::diag::Formatter diagFormatter;
4540 *warnings = diagFormatter.Format(program.Diagnostics()).Plain();
4551#ifdef SK_ENABLE_WGSL_VALIDATION
4556 std::string wgslString = wgsl.
str();
4557 std::string warnings;
4558 result = validate_wgsl(*program.
fContext->fErrors, wgslString, &warnings);
4559 if (!warnings.empty()) {
4560 out.writeText(
"/* Tint reported warnings. */\n\n");
4562 out.writeString(wgslString);
4568 program.
fContext->fErrors->setSource(std::string_view());
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
static struct Initializer initializer
#define SkDEBUGFAIL(message)
#define SkDEBUGFAILF(fmt,...)
#define SkASSERTF(cond, fmt,...)
#define SK_MAKE_BITMASK_OPS(E)
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
#define INHERITED(method,...)
constexpr int SK_SAMPLEMASK_BUILTIN
constexpr int SK_WORKGROUPID_BUILTIN
constexpr int SK_CLOCKWISE_BUILTIN
constexpr int SK_VERTEXID_BUILTIN
constexpr int SK_LASTFRAGCOLOR_BUILTIN
constexpr int SK_GLOBALINVOCATIONID_BUILTIN
constexpr int SK_POSITION_BUILTIN
constexpr int SK_LOCALINVOCATIONID_BUILTIN
constexpr int SK_INSTANCEID_BUILTIN
constexpr int SK_NUMWORKGROUPS_BUILTIN
constexpr int SK_POINTSIZE_BUILTIN
constexpr int SK_FRAGCOORD_BUILTIN
constexpr int SK_LOCALINVOCATIONINDEX_BUILTIN
constexpr int SK_SAMPLEMASKIN_BUILTIN
constexpr size_t SkToSizeT(S x)
const std::unique_ptr< Type > fFloat2
const std::unique_ptr< Type > fHalf4
const std::unique_ptr< Type > fUShort
const std::unique_ptr< Type > fUInt
const std::unique_ptr< Type > fBool
static constexpr float kSharpenTexturesBias
static const Expression * GetConstantValueOrNull(const Expression &value)
const BuiltinTypes & fTypes
void error(Position position, std::string_view msg)
virtual const Type & type() const
const FunctionDeclaration & declaration() const
virtual void writeText(const char *s)=0
static std::unique_ptr< Expression > Make(const Context &context, Position pos, Operator op, std::unique_ptr< Expression > base)
void writeText(const char *s) override
const std::string & str() const
static std::string MaskString(const ComponentArray &inComponents)
virtual bool visitExpression(typename T::Expression &expression)
virtual bool visitProgramElement(typename T::ProgramElement &programElement)
virtual bool isVector() const
virtual size_t slotCount() const
virtual ~LValue()=default
virtual std::string store(const std::string &value)=0
virtual std::string load()=0
PointerLValue(std::string name)
std::string store(const std::string &value) override
std::string load() override
std::string store(const std::string &value) override
SwizzleLValue(const Context &ctx, std::string name, const Type &t, const ComponentArray &c)
std::string load() override
VectorComponentLValue(std::string name)
std::string store(const std::string &value) override
std::string load() override
WGSLCodeGenerator(const Context *context, const ShaderCaps *caps, const Program *program, OutputStream *out)
bool generateCode() override
constexpr T * begin() const
constexpr T & back() const
constexpr T * end() const
constexpr bool empty() const
constexpr size_t size() const
void resize(size_t count)
void reserve_exact(int n)
V * find(const K &key) const
bool contains(const T &item) const
const EmbeddedViewParams * params
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
EMSCRIPTEN_KEEPALIVE void empty()
FlutterSemanticsFlag flags
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static const uint8_t buffer[]
unsigned useCenter Optional< SkMatrix > matrix
bool IsCompileTimeConstant(const Expression &expr)
bool IsConstantExpression(const Expression &expr)
bool SwitchCaseContainsUnconditionalExit(const Statement &stmt)
bool IsTrivialExpression(const Expression &expr)
bool HasSideEffects(const Expression &expr)
std::string printf(const char *fmt,...) SK_PRINTF_LIKE(1
std::string void void auto Separator()
std::string void appendf(std::string *str, const char *fmt,...) SK_PRINTF_LIKE(2
static constexpr char kInverse3x3[]
static bool all_arguments_constant(const ExpressionArray &arguments)
static bool binary_op_is_ambiguous_in_wgsl(Operator op)
static constexpr char kInverse2x2[]
void write_stringstream(const StringStream &s, OutputStream &out)
skia_private::STArray< 2, std::unique_ptr< Statement > > StatementArray
static constexpr char kInverse4x4[]
bool ToWGSL(Program &program, const ShaderCaps *caps, OutputStream &out)
static bool is_nontrivial_expression(const Expression &expr)
static const char * operator_name(Operator op)
SkEnumBitMask< SkSL::LayoutFlag > LayoutFlags
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core counts
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not set
constexpr bool starts_with(std::string_view str, std::string_view prefix)
std::string to_string(float value)
static bool IsRuntimeShader(ProgramKind kind)
static bool IsVertex(ProgramKind kind)
static bool IsFragment(ProgramKind kind)
static bool IsCompute(ProgramKind kind)
bool fOutputSecondaryColor
ElementsCollection elements() const
std::shared_ptr< Context > fContext
std::unique_ptr< ProgramUsage > fUsage
ProgramInterface fInterface
std::unique_ptr< std::string > fSource
std::unique_ptr< ProgramConfig > fConfig
skia_private::THashMap< const FunctionDeclaration *, WGSLFunctionDependencies > DepsMap
bool fPixelLocalExtension
#define TRACE_EVENT0(category_group, name)