84#include <initializer_list>
92#ifdef SK_ENABLE_WGSL_VALIDATION
94#include "src/tint/lang/wgsl/reader/options.h"
95#include "src/tint/lang/wgsl/extension.h"
107enum class WGSLFunctionDependency : uint8_t {
109 kPipelineInputs = 1 << 0,
110 kPipelineOutputs = 1 << 1,
139 kLocalInvocationIndex,
158 WGSLFunctionDependencies>;
181 void preprocessProgram();
184 void write(std::string_view
s);
185 void writeLine(std::string_view
s = std::string_view());
189 void writePipelineIODeclaration(
const Layout& layout,
191 std::string_view
name,
193 void writeUserDefinedIODecl(
const Layout& layout,
195 std::string_view
name,
197 void writeBuiltinIODecl(
const Type&
type,
198 std::string_view
name,
201 void writeVariableDecl(
const Layout& layout,
203 std::string_view
name,
217 void writeBlock(
const Block&
b);
219 void writeExpressionStatement(
const Expression& expr);
226 std::string_view switchValue);
234 class VectorComponentLValue;
235 std::unique_ptr<LValue> makeLValue(
const Expression&
e);
238 std::string variablePrefix(
const Variable& v);
245 std::string assembleExpression(
const Expression&
e, Precedence parentPrecedence);
246 std::string assembleBinaryExpression(
const BinaryExpression&
b, Precedence parentPrecedence);
250 const Type& resultType,
251 Precedence parentPrecedence);
253 std::string assembleFunctionCall(
const FunctionCall&
call, Precedence parentPrecedence);
255 std::string assembleLiteral(
const Literal& l);
256 std::string assemblePostfixExpression(
const PostfixExpression&
p, Precedence parentPrecedence);
257 std::string assemblePrefixExpression(
const PrefixExpression&
p, Precedence parentPrecedence);
258 std::string assembleSwizzle(
const Swizzle& swizzle);
259 std::string assembleTernaryExpression(
const TernaryExpression& t, Precedence parentPrecedence);
261 std::string assembleName(std::string_view
name);
263 std::string assembleIncrementExpr(
const Type&
type);
268 Precedence parentPrecedence);
269 std::string assembleSimpleIntrinsic(std::string_view intrinsicName,
const FunctionCall&
call);
270 std::string assembleUnaryOpIntrinsic(
Operator op,
272 Precedence parentPrecedence);
273 std::string assembleBinaryOpIntrinsic(
Operator op,
275 Precedence parentPrecedence);
276 std::string assembleVectorizedIntrinsic(std::string_view intrinsicName,
278 std::string assembleOutAssignedIntrinsic(std::string_view intrinsicName,
279 std::string_view returnFieldName,
280 std::string_view outFieldName,
282 std::string assemblePartialSampleCall(std::string_view intrinsicName,
286 std::string assembleComponentwiseMatrixBinary(
const Type& leftType,
287 const Type& rightType,
288 const std::string&
left,
289 const std::string&
right,
301 std::string assembleEqualityExpression(
const Type&
left,
302 const std::string& leftName,
304 const std::string& rightName,
306 Precedence parentPrecedence);
310 Precedence parentPrecedence);
313 std::string writeScratchVar(
const Type&
type,
const std::string&
value =
"");
317 std::string writeScratchLet(
const std::string& expr,
bool isCompileTimeConstant =
false);
318 std::string writeScratchLet(
const Expression& expr, Precedence parentPrecedence);
323 std::string writeNontrivialScratchLet(
const Expression& expr, Precedence parentPrecedence);
338 bool needsStageInputStruct()
const;
339 void writeStageInputStruct();
340 bool needsStageOutputStruct()
const;
341 void writeStageOutputStruct();
342 void writeUniformsAndBuffers();
343 void prepareUniformPolyfillsForInterfaceBlock(
const InterfaceBlock* interfaceBlock,
344 std::string_view instanceName,
347 void writeUniformPolyfills();
349 void writeTextureOrSampler(
const Variable& var,
352 std::string_view wgslType);
365 void writeNonBlockUniformsForTests();
381 ProgramRequirements fRequirements;
387 bool fWrittenInverse2 =
false;
388 bool fWrittenInverse3 =
false;
389 bool fWrittenInverse4 =
false;
396 struct FieldPolyfillInfo {
398 std::string fReplacementName;
399 bool fIsArray =
false;
400 bool fIsMatrix =
false;
401 bool fWasAccessed =
false;
404 FieldPolyfillMap fFieldPolyfillMap;
407 int fIndentation = 0;
408 bool fAtLineStart =
false;
409 bool fHasUnconditionalReturn =
false;
410 bool fAtFunctionScope =
false;
411 int fConditionalScopeDepth = 0;
416 int fScratchCount = 0;
423static constexpr char kSamplerSuffix[] =
"_Sampler";
424static constexpr char kTextureSuffix[] =
"_Texture";
427enum class PtrAddressSpace {
435 case Operator::Kind::LOGICALXOR:
return " != ";
436 default:
return op.operatorName();
440bool is_reserved_word(std::string_view
word) {
504 "texture_cube_array",
505 "texture_multisampled_2d",
506 "texture_storage_1d",
507 "texture_storage_2d",
508 "texture_storage_2d_array",
509 "texture_storage_3d",
533 "local_invocation_id",
534 "local_invocation_index",
535 "global_invocation_id",
709std::string_view pipeline_struct_prefix(
ProgramKind kind) {
723std::string_view address_space_to_str(PtrAddressSpace addressSpace) {
724 switch (addressSpace) {
725 case PtrAddressSpace::kFunction:
727 case PtrAddressSpace::kPrivate:
729 case PtrAddressSpace::kStorage:
733 return "unsupported";
736std::string_view to_scalar_type(
const Type&
type) {
738 switch (
type.numberKind()) {
760std::string to_wgsl_type(
const Context& context,
const Type& raw,
const Layout* layout =
nullptr) {
761 const Type&
type = raw.resolve().scalarTypeForLiteral();
762 switch (
type.typeKind()) {
764 return std::string(to_scalar_type(
type));
768 return "atomic<u32>";
771 std::string_view ct = to_scalar_type(
type.componentType());
775 std::string_view ct = to_scalar_type(
type.componentType());
777 type.columns(),
type.rows(), (
int)ct.length(), ct.data());
780 std::string
result =
"array<" + to_wgsl_type(context,
type.componentType(), layout);
781 if (!
type.isUnsizedArray()) {
788 if (
type.matches(*context.fTypes.fWriteOnlyTexture2D)) {
789 std::string
result =
"texture_storage_2d<";
793 switch (pixelFormat.value()) {
795 return result +
"rgba8unorm, write>";
798 return result +
"rgba32float, write>";
801 return result +
"r32float, write>";
808 if (
type.matches(*context.fTypes.fReadOnlyTexture2D)) {
809 return "texture_2d<f32>";
816 return std::string(
type.name());
819std::string to_ptr_type(
const Context& context,
822 PtrAddressSpace addressSpace = PtrAddressSpace::kFunction) {
823 return "ptr<" + std::string(address_space_to_str(addressSpace)) +
", " +
824 to_wgsl_type(context,
type, layout) +
'>';
830 case Builtin::kVertexIndex:
831 return "@builtin(vertex_index)";
832 case Builtin::kInstanceIndex:
833 return "@builtin(instance_index)";
834 case Builtin::kPosition:
835 return "@builtin(position)";
836 case Builtin::kLastFragColor:
838 case Builtin::kFrontFacing:
839 return "@builtin(front_facing)";
840 case Builtin::kSampleIndex:
841 return "@builtin(sample_index)";
842 case Builtin::kFragDepth:
843 return "@builtin(frag_depth)";
844 case Builtin::kSampleMask:
845 case Builtin::kSampleMaskIn:
846 return "@builtin(sample_mask)";
847 case Builtin::kLocalInvocationId:
848 return "@builtin(local_invocation_id)";
849 case Builtin::kLocalInvocationIndex:
850 return "@builtin(local_invocation_index)";
851 case Builtin::kGlobalInvocationId:
852 return "@builtin(global_invocation_id)";
853 case Builtin::kWorkgroupId:
854 return "@builtin(workgroup_id)";
855 case Builtin::kNumWorkgroups:
856 return "@builtin(num_workgroups)";
862 return "unsupported";
868 case Builtin::kVertexIndex:
870 case Builtin::kInstanceIndex:
872 case Builtin::kPosition:
874 case Builtin::kLastFragColor:
876 case Builtin::kFrontFacing:
878 case Builtin::kSampleIndex:
880 case Builtin::kFragDepth:
882 case Builtin::kSampleMask:
884 case Builtin::kSampleMaskIn:
886 case Builtin::kLocalInvocationId:
888 case Builtin::kLocalInvocationIndex:
890 case Builtin::kGlobalInvocationId:
892 case Builtin::kWorkgroupId:
894 case Builtin::kNumWorkgroups:
901 return "unsupported";
908std::optional<std::string_view> needs_builtin_type_conversion(
const Variable& v) {
909 switch (v.layout().fBuiltin) {
923std::optional<WGSLCodeGenerator::Builtin> builtin_from_sksl_name(
int builtin) {
929 return Builtin::kPosition;
931 return Builtin::kVertexIndex;
933 return Builtin::kInstanceIndex;
935 return Builtin::kLastFragColor;
941 return Builtin::kFrontFacing;
943 return Builtin::kSampleMaskIn;
945 return Builtin::kSampleMask;
947 return Builtin::kNumWorkgroups;
949 return Builtin::kWorkgroupId;
951 return Builtin::kLocalInvocationId;
953 return Builtin::kGlobalInvocationId;
955 return Builtin::kLocalInvocationIndex;
967 case Delim::kSemicolon:
979class FunctionDependencyResolver :
public ProgramVisitor {
981 using Deps = WGSLFunctionDependencies;
984 FunctionDependencyResolver(
const Program*
p,
985 const FunctionDeclaration*
f,
986 DepsMap* programDependencyMap)
987 : fProgram(
p), fFunction(
f), fDependencyMap(programDependencyMap) {}
991 this->visit(*fProgram);
996 bool visitProgramElement(
const ProgramElement&
p)
override {
998 if (
p.is<FunctionDefinition>() && &
p.as<FunctionDefinition>().declaration() == fFunction) {
1005 bool visitExpression(
const Expression&
e)
override {
1006 if (
e.is<VariableReference>()) {
1007 const VariableReference& v =
e.as<VariableReference>();
1009 ModifierFlags
flags = v.variable()->modifierFlags();
1011 fDeps |= WGSLFunctionDependency::kPipelineInputs;
1014 fDeps |= WGSLFunctionDependency::kPipelineOutputs;
1017 }
else if (
e.is<FunctionCall>()) {
1021 const FunctionCall& callee =
e.as<FunctionCall>();
1024 Deps* found = fDependencyMap->find(&callee.function());
1031 fDependencyMap->set(fFunction, fDeps);
1035 FunctionDependencyResolver resolver(fProgram, &callee.function(), fDependencyMap);
1036 Deps calleeDeps = resolver.resolve();
1040 fDependencyMap->set(&callee.function(), calleeDeps);
1043 fDeps |= calleeDeps;
1049 const Program*
const fProgram;
1050 const FunctionDeclaration*
const fFunction;
1051 DepsMap*
const fDependencyMap;
1057WGSLCodeGenerator::ProgramRequirements resolve_program_requirements(
const Program* program) {
1058 WGSLCodeGenerator::ProgramRequirements requirements;
1060 for (
const ProgramElement*
e : program->elements()) {
1061 switch (
e->kind()) {
1063 const FunctionDeclaration& decl =
e->as<FunctionDefinition>().declaration();
1065 FunctionDependencyResolver resolver(program, &decl, &requirements.fDependencies);
1066 requirements.fDependencies.set(&decl, resolver.resolve());
1069 case ProgramElement::Kind::kGlobalVar: {
1070 const GlobalVarDeclaration& decl =
e->as<GlobalVarDeclaration>();
1071 if (decl.varDeclaration().var()->modifierFlags().isPixelLocal()) {
1072 requirements.fPixelLocalExtension =
true;
1081 return requirements;
1084void collect_pipeline_io_vars(
const Program* program,
1087 for (
const ProgramElement*
e : program->elements()) {
1088 if (
e->is<GlobalVarDeclaration>()) {
1089 const Variable* v =
e->as<GlobalVarDeclaration>().varDeclaration().var();
1090 if (v->modifierFlags() & ioType) {
1093 }
else if (
e->is<InterfaceBlock>()) {
1094 const Variable* v =
e->as<InterfaceBlock>().var();
1095 if (v->modifierFlags() & ioType) {
1102bool is_in_global_uniforms(
const Variable& var) {
1104 return var.modifierFlags().isUniform() &&
1105 !var.type().isOpaque() &&
1106 !var.interfaceBlock();
1135 return fName +
" = " +
value +
";";
1153 return fName +
" = " +
value +
";";
1186 for (int8_t component : fComponents) {
1188 used[component] =
true;
1192 for (
int index = 0; index < fullSlotCount; ++index) {
1199 fReintegrationSwizzle.
resize(fullSlotCount);
1200 int reintegrateIndex = 0;
1203 auto refillUntouchedSlots = [&] {
1204 for (
int index = 0; index < fullSlotCount; ++index) {
1206 fReintegrationSwizzle[index] = reintegrateIndex++;
1212 auto insertNewValuesIntoSlots = [&] {
1213 for (
int index = 0; index < fComponents.size(); ++index) {
1214 fReintegrationSwizzle[fComponents[index]] = reintegrateIndex++;
1222 fReintegrateNewValueFirst =
true;
1223 insertNewValuesIntoSlots();
1224 refillUntouchedSlots();
1226 fReintegrateNewValueFirst =
false;
1227 refillUntouchedSlots();
1228 insertNewValuesIntoSlots();
1238 std::string
result = fName;
1241 if (fUntouchedComponents.
empty()) {
1246 }
else if (fReintegrateNewValueFirst) {
1248 result += to_wgsl_type(fContext, fType);
1260 result += to_wgsl_type(fContext, fType);
1288 bool fReintegrateNewValueFirst =
false;
1296 this->preprocessProgram();
1300 this->writeEnables();
1301 this->writeStageInputStruct();
1302 this->writeStageOutputStruct();
1303 this->writeUniformsAndBuffers();
1304 this->writeNonBlockUniformsForTests();
1312 this->writeProgramElement(*
e);
1326 this->writeEntryPoint(*mainFunc);
1333 this->writeUniformPolyfills();
1338void WGSLCodeGenerator::writeUniformPolyfills() {
1340 if (fFieldPolyfillMap.
empty()) {
1349 fFieldPolyfillMap.
foreach([&](
const FieldPolyfillMap::Pair& pair) {
1354 orderedFields.
end(),
1355 [](
const FieldPolyfillMap::Pair*
a,
const FieldPolyfillMap::Pair*
b) {
1356 return a->second.fReplacementName < b->second.fReplacementName;
1360 bool writtenUniformMatrixPolyfill[5][5] = {};
1361 bool writtenUniformRowPolyfill[5] = {};
1362 bool anyFieldAccessed =
false;
1363 for (
const FieldPolyfillMap::Pair* pair : orderedFields) {
1364 const auto& [field,
info] = *pair;
1365 const Type* fieldType = field->fType;
1366 const Layout* fieldLayout = &field->fLayout;
1368 if (
info.fIsArray) {
1369 fieldType = &fieldType->componentType();
1370 if (!writtenArrayElementPolyfill.
contains(fieldType)) {
1371 writtenArrayElementPolyfill.
add(fieldType);
1372 this->write(
"struct _skArrayElement_");
1373 this->write(fieldType->abbreviatedName());
1374 this->writeLine(
" {");
1376 if (
info.fIsMatrix) {
1378 this->write(
" e : _skMatrix");
1383 this->write(
" @size(16) e : ");
1384 this->writeLine(to_wgsl_type(
fContext, *fieldType, fieldLayout));
1386 this->writeLine(
"};");
1390 if (
info.fIsMatrix) {
1394 int c = fieldType->columns();
1395 int r = fieldType->rows();
1396 if (!writtenUniformRowPolyfill[r]) {
1397 writtenUniformRowPolyfill[r] =
true;
1399 this->write(
"struct _skRow");
1401 this->writeLine(
" {");
1402 this->write(
" @size(16) r : vec");
1405 this->write(to_wgsl_type(
fContext, fieldType->componentType(), fieldLayout));
1406 this->writeLine(
">");
1407 this->writeLine(
"};");
1410 if (!writtenUniformMatrixPolyfill[c][r]) {
1411 writtenUniformMatrixPolyfill[c][r] =
true;
1413 this->write(
"struct _skMatrix");
1416 this->writeLine(
" {");
1417 this->write(
" c : array<_skRow");
1421 this->writeLine(
">");
1422 this->writeLine(
"};");
1427 if (!
info.fWasAccessed) {
1430 anyFieldAccessed =
true;
1431 this->write(
"var<private> ");
1432 this->write(
info.fReplacementName);
1435 const Type& interfaceBlockType =
info.fInterfaceBlock->var()->type();
1436 if (interfaceBlockType.isArray()) {
1437 this->write(
"array<");
1438 this->write(to_wgsl_type(
fContext, *field->fType, fieldLayout));
1443 this->write(to_wgsl_type(
fContext, *field->fType, fieldLayout));
1445 this->writeLine(
";");
1450 if (!anyFieldAccessed) {
1454 this->writeLine(
"fn _skInitializePolyfilledUniforms() {");
1457 for (
const FieldPolyfillMap::Pair* pair : orderedFields) {
1459 const auto& [field,
info] = *pair;
1460 if (!
info.fWasAccessed) {
1465 std::string_view instanceName =
info.fInterfaceBlock->instanceName();
1466 const Type& interfaceBlockType =
info.fInterfaceBlock->var()->type();
1467 if (instanceName.empty()) {
1468 instanceName = fInterfaceBlockNameMap[&interfaceBlockType.componentType()];
1473 int numIBElements = interfaceBlockType.isArray() ? interfaceBlockType.columns() : 1;
1474 for (
int ibIdx = 0; ibIdx < numIBElements; ++ibIdx) {
1475 this->write(
info.fReplacementName);
1476 if (interfaceBlockType.isArray()) {
1483 const Type* fieldType = field->fType;
1484 const Layout* fieldLayout = &field->fLayout;
1486 int numArrayElements;
1487 if (
info.fIsArray) {
1488 this->write(to_wgsl_type(
fContext, *fieldType, fieldLayout));
1490 numArrayElements = fieldType->columns();
1491 fieldType = &fieldType->componentType();
1493 numArrayElements = 1;
1497 for (
int arrayIdx = 0; arrayIdx < numArrayElements; arrayIdx++) {
1498 this->write(arraySeparator());
1500 std::string fieldName{instanceName};
1501 if (interfaceBlockType.isArray()) {
1507 fieldName += this->assembleName(field->fName);
1509 if (
info.fIsArray) {
1515 if (
info.fIsMatrix) {
1516 this->write(to_wgsl_type(
fContext, *fieldType, fieldLayout));
1518 int numColumns = fieldType->columns();
1520 for (
int column = 0; column < numColumns; column++) {
1521 this->write(matrixSeparator());
1522 this->write(fieldName);
1529 this->write(fieldName);
1533 if (
info.fIsArray) {
1537 this->writeLine(
";");
1542 this->writeLine(
"}");
1546void WGSLCodeGenerator::preprocessProgram() {
1547 fRequirements = resolve_program_requirements(&
fProgram);
1552void WGSLCodeGenerator::write(std::string_view
s) {
1556#if defined(SK_DEBUG) || defined(SKSL_STANDALONE)
1558 for (
int i = 0;
i < fIndentation;
i++) {
1564 fAtLineStart =
false;
1567void WGSLCodeGenerator::writeLine(std::string_view
s) {
1570 fAtLineStart =
true;
1573void WGSLCodeGenerator::finishLine() {
1574 if (!fAtLineStart) {
1579std::string WGSLCodeGenerator::assembleName(std::string_view
name) {
1587 is_reserved_word(
name))
1588 ? std::string(
"R_") + std::string(
name)
1592void WGSLCodeGenerator::writeVariableDecl(
const Layout& layout,
1594 std::string_view
name,
1595 Delimiter delimiter) {
1596 this->write(this->assembleName(
name));
1597 this->write(
": " + to_wgsl_type(
fContext,
type, &layout));
1598 this->writeLine(delimiter_to_str(delimiter));
1601void WGSLCodeGenerator::writePipelineIODeclaration(
const Layout& layout,
1603 std::string_view
name,
1604 Delimiter delimiter) {
1617 if (layout.fLocation >= 0) {
1618 this->writeUserDefinedIODecl(layout,
type,
name, delimiter);
1621 if (layout.fBuiltin >= 0) {
1627 auto builtin = builtin_from_sksl_name(layout.fBuiltin);
1628 if (builtin.has_value()) {
1629 this->writeBuiltinIODecl(
type,
name, *builtin, delimiter);
1636void WGSLCodeGenerator::writeUserDefinedIODecl(
const Layout& layout,
1638 std::string_view
name,
1639 Delimiter delimiter) {
1640 this->write(
"@location(" +
std::to_string(layout.fLocation) +
") ");
1644 this->write(
"@blend_src(" +
std::to_string(layout.fIndex) +
") ");
1649 if (
type.isInteger() || (
type.isVector() &&
type.componentType().isInteger())) {
1650 this->write(
"@interpolate(flat) ");
1653 this->writeVariableDecl(layout,
type,
name, delimiter);
1656void WGSLCodeGenerator::writeBuiltinIODecl(
const Type&
type,
1657 std::string_view
name,
1659 Delimiter delimiter) {
1660 this->write(wgsl_builtin_name(builtin));
1662 this->write(this->assembleName(
name));
1664 this->write(wgsl_builtin_type(builtin));
1665 this->writeLine(delimiter_to_str(delimiter));
1668void WGSLCodeGenerator::writeFunction(
const FunctionDefinition&
f) {
1669 const FunctionDeclaration& decl =
f.declaration();
1670 fHasUnconditionalReturn =
false;
1671 fConditionalScopeDepth = 0;
1674 fAtFunctionScope =
true;
1682 paramNeedsDedicatedStorage.
push_back_n(decl.parameters().size(),
true);
1684 for (
size_t index = 0; index < decl.parameters().
size(); ++index) {
1685 const Variable& param = *decl.parameters()[index];
1686 if (param.type().isOpaque() || param.name().empty()) {
1688 paramNeedsDedicatedStorage[index] =
false;
1697 paramNeedsDedicatedStorage[index] =
false;
1701 this->writeFunctionDeclaration(decl, paramNeedsDedicatedStorage);
1702 this->writeLine(
" {");
1708 for (
size_t index = 0; index < decl.parameters().
size(); ++index) {
1709 if (paramNeedsDedicatedStorage[index]) {
1710 const Variable& param = *decl.parameters()[index];
1711 this->write(
"var ");
1712 this->write(this->assembleName(param.mangledName()));
1713 this->write(
" = _skParam");
1715 this->writeLine(
";");
1719 this->writeBlock(
f.body()->as<
Block>());
1722 SkASSERT(fConditionalScopeDepth == 0);
1723 if (!fHasUnconditionalReturn && !
f.declaration().returnType().isVoid()) {
1724 this->write(
"return ");
1725 this->write(to_wgsl_type(
fContext,
f.declaration().returnType()));
1726 this->writeLine(
"();");
1730 this->writeLine(
"}");
1733 fAtFunctionScope =
false;
1736void WGSLCodeGenerator::writeFunctionDeclaration(
const FunctionDeclaration& decl,
1739 if (decl.isMain()) {
1740 this->write(
"_skslMain(");
1742 this->write(this->assembleName(decl.mangledName()));
1746 if (this->writeFunctionDependencyParams(decl)) {
1749 for (
size_t index = 0; index < decl.parameters().
size(); ++index) {
1750 this->write(separator());
1752 const Variable& param = *decl.parameters()[index];
1753 if (param.type().isOpaque()) {
1754 SkASSERT(!paramNeedsDedicatedStorage[index]);
1755 if (param.type().isSampler()) {
1757 this->write(param.name());
1758 this->write(kTextureSuffix);
1759 this->write(
": texture_2d<f32>, ");
1760 this->write(param.name());
1761 this->write(kSamplerSuffix);
1762 this->write(
": sampler");
1765 this->write(param.name());
1767 this->write(to_wgsl_type(
fContext, param.type(), ¶m.layout()));
1770 if (paramNeedsDedicatedStorage[index] || param.name().empty()) {
1774 this->write(
"_skParam");
1778 this->write(this->assembleName(param.name()));
1783 this->write(to_ptr_type(
fContext, param.type(), ¶m.layout()));
1785 this->write(to_wgsl_type(
fContext, param.type(), ¶m.layout()));
1790 if (!decl.returnType().isVoid()) {
1791 this->write(
" -> ");
1792 this->write(to_wgsl_type(
fContext, decl.returnType()));
1796void WGSLCodeGenerator::writeEntryPoint(
const FunctionDefinition&
main) {
1800#if defined(SKSL_STANDALONE)
1805 this->writeLine(
"@fragment fn main(@location(0) _coords: vec2<f32>) -> "
1806 "@location(0) vec4<f32> {");
1808 this->writeLine(
"return _skslMain(_coords);");
1810 this->writeLine(
"}");
1820 this->write(
"@vertex");
1822 this->write(
"@fragment");
1824 this->write(
"@compute @workgroup_size(");
1836 this->write(
" fn main(");
1838 if (this->needsStageInputStruct()) {
1839 this->write(
"_stageIn: ");
1844 if (this->needsStageOutputStruct()) {
1845 this->write(
") -> ");
1847 this->writeLine(
"Out {");
1849 this->writeLine(
") {");
1853 for (
const auto& [field,
info] : fFieldPolyfillMap) {
1854 if (
info.fWasAccessed) {
1855 this->writeLine(
"_skInitializePolyfilledUniforms();");
1860 if (this->needsStageOutputStruct()) {
1861 this->write(
"var _stageOut: ");
1863 this->writeLine(
"Out;");
1866#if defined(SKSL_STANDALONE)
1874 this->write(
"_stageOut.sk_FragColor = ");
1880 this->write(
"_skslMain(");
1884 if (*deps & WGSLFunctionDependency::kPipelineInputs) {
1885 this->write(separator());
1886 this->write(
"_stageIn");
1888 if (*deps & WGSLFunctionDependency::kPipelineOutputs) {
1889 this->write(separator());
1890 this->write(
"&_stageOut");
1894#if defined(SKSL_STANDALONE)
1895 if (
const Variable* v =
main.declaration().getMainCoordsParameter()) {
1902 type.description());
1905 this->write(separator());
1906 this->write(
"/*fragcoord*/ vec2<f32>()");
1910 this->writeLine(
");");
1912 if (this->needsStageOutputStruct()) {
1914 this->writeLine(
"return _stageOut;");
1918 this->writeLine(
"}");
1921void WGSLCodeGenerator::writeStatement(
const Statement&
s) {
1923 case Statement::Kind::kBlock:
1924 this->writeBlock(
s.as<
Block>());
1926 case Statement::Kind::kBreak:
1927 this->writeLine(
"break;");
1930 this->writeLine(
"continue;");
1933 this->writeLine(
"discard;");
1935 case Statement::Kind::kDo:
1936 this->writeDoStatement(
s.as<DoStatement>());
1938 case Statement::Kind::kExpression:
1939 this->writeExpressionStatement(*
s.as<ExpressionStatement>().expression());
1941 case Statement::Kind::kFor:
1942 this->writeForStatement(
s.as<ForStatement>());
1944 case Statement::Kind::kIf:
1945 this->writeIfStatement(
s.as<IfStatement>());
1948 this->writeLine(
";");
1950 case Statement::Kind::kReturn:
1951 this->writeReturnStatement(
s.as<ReturnStatement>());
1953 case Statement::Kind::kSwitch:
1954 this->writeSwitchStatement(
s.as<SwitchStatement>());
1956 case Statement::Kind::kSwitchCase:
1957 SkDEBUGFAIL(
"switch-case statements should only be present inside a switch");
1959 case Statement::Kind::kVarDeclaration:
1960 this->writeVarDeclaration(
s.as<VarDeclaration>());
1965void WGSLCodeGenerator::writeStatements(
const StatementArray& statements) {
1966 for (
const auto&
s : statements) {
1967 if (!
s->isEmpty()) {
1968 this->writeStatement(*
s);
1974void WGSLCodeGenerator::writeBlock(
const Block&
b) {
1977 bool isScope =
b.isScope() ||
b.isEmpty();
1979 this->writeLine(
"{");
1982 this->writeStatements(
b.children());
1985 this->writeLine(
"}");
1989void WGSLCodeGenerator::writeExpressionStatement(
const Expression& expr) {
1994 (void)this->assembleExpression(expr, Precedence::kStatement);
1997void WGSLCodeGenerator::writeDoStatement(
const DoStatement&
s) {
2006 ++fConditionalScopeDepth;
2011 this->writeLine(
"loop {");
2013 this->writeStatement(*
s.statement());
2016 this->writeLine(
"continuing {");
2018 std::string breakIfExpr = this->assembleExpression(*invertedTestExpr, Precedence::kExpression);
2019 this->write(
"break if ");
2020 this->write(breakIfExpr);
2021 this->writeLine(
";");
2023 this->writeLine(
"}");
2025 this->writeLine(
"}");
2027 --fConditionalScopeDepth;
2030void WGSLCodeGenerator::writeForStatement(
const ForStatement&
s) {
2040 ++fConditionalScopeDepth;
2042 if (
s.initializer()) {
2043 this->writeLine(
"{");
2045 this->writeStatement(*
s.initializer());
2049 this->writeLine(
"loop {");
2052 if (
s.unrollInfo()) {
2053 if (
s.unrollInfo()->fCount <= 0) {
2067 this->writeStatement(*
s.statement());
2069 this->writeLine(
"continuing {");
2073 this->writeExpressionStatement(*
s.next());
2081 std::string breakIfExpr =
2082 this->assembleExpression(*invertedTestExpr, Precedence::kExpression);
2083 this->write(
"break if ");
2084 this->write(breakIfExpr);
2085 this->writeLine(
";");
2089 this->writeLine(
"}");
2106 std::string testExpr = this->assembleExpression(*
s.test(), Precedence::kExpression);
2108 this->write(testExpr);
2109 this->writeLine(
" {");
2112 this->writeStatement(*
s.statement());
2116 this->writeLine(
"} else {");
2119 this->writeLine(
"break;");
2122 this->writeLine(
"}");
2125 this->writeStatement(*
s.statement());
2130 this->writeLine(
"continuing {");
2132 this->writeExpressionStatement(*
s.next());
2135 this->writeLine(
"}");
2141 this->writeLine(
"}");
2143 if (
s.initializer()) {
2146 this->writeLine(
"}");
2149 --fConditionalScopeDepth;
2152void WGSLCodeGenerator::writeIfStatement(
const IfStatement&
s) {
2153 ++fConditionalScopeDepth;
2155 std::string testExpr = this->assembleExpression(*
s.test(), Precedence::kExpression);
2157 this->write(testExpr);
2158 this->writeLine(
" {");
2160 this->writeStatement(*
s.ifTrue());
2164 this->writeLine(
"} else {");
2166 this->writeStatement(*
s.ifFalse());
2170 this->writeLine(
"}");
2172 --fConditionalScopeDepth;
2175void WGSLCodeGenerator::writeReturnStatement(
const ReturnStatement&
s) {
2176 fHasUnconditionalReturn |= (fConditionalScopeDepth == 0);
2178 std::string expr =
s.expression()
2179 ? this->assembleExpression(*
s.expression(), Precedence::kExpression)
2181 this->write(
"return ");
2188 for (
const SwitchCase*
const sc : cases) {
2189 this->write(separator());
2190 if (sc->isDefault()) {
2191 this->write(
"default");
2199 if (!cases.
empty()) {
2202 return sc->statement()->isEmpty();
2206 this->write(
"case ");
2207 this->writeSwitchCaseList(cases);
2208 this->writeLine(
" {");
2212 this->writeStatement(*cases.
back()->statement());
2216 this->writeLine(
"}");
2221 std::string_view switchValue) {
2223 if (cases.
size() < 2) {
2224 this->writeSwitchCases(cases);
2229 this->write(
"case ");
2230 this->writeSwitchCaseList(cases);
2231 this->writeLine(
" {");
2235 const size_t secondToLastCaseIndex = cases.
size() - 2;
2236 const size_t lastCaseIndex = cases.
size() - 1;
2238 for (
size_t index = 0; index < cases.
size(); ++index) {
2239 const SwitchCase& sc = *cases[index];
2240 if (index < lastCaseIndex) {
2247 this->write(fallthroughVar);
2248 this->write(
" || ");
2250 this->write(switchValue);
2251 this->write(
" == ");
2253 this->writeLine(
" {");
2262 this->writeStatement(*sc.statement());
2265 if (index < secondToLastCaseIndex) {
2270 this->write(fallthroughVar);
2271 this->write(
" = true; ");
2273 this->writeLine(
"// fallthrough");
2276 this->writeLine(
"}");
2282 this->writeStatement(*sc.statement());
2288 this->writeLine(
"}");
2291void WGSLCodeGenerator::writeSwitchStatement(
const SwitchStatement&
s) {
2306 std::string valueExpr = this->writeNontrivialScratchLet(*
s.value(), Precedence::kExpression);
2307 this->write(
"switch ");
2308 this->write(valueExpr);
2309 this->writeLine(
" {");
2315 bool previousCaseFellThrough =
false;
2316 bool foundNativeDefault =
false;
2317 [[maybe_unused]]
bool foundFallthroughDefault =
false;
2319 const int lastSwitchCaseIdx =
s.cases().size() - 1;
2320 for (
int index = 0; index <= lastSwitchCaseIdx; ++index) {
2321 const SwitchCase& sc =
s.cases()[index]->as<SwitchCase>();
2323 if (sc.statement()->isEmpty()) {
2326 if (previousCaseFellThrough) {
2328 foundFallthroughDefault |= sc.isDefault();
2331 foundNativeDefault |= sc.isDefault();
2338 if (previousCaseFellThrough) {
2341 foundFallthroughDefault |= sc.isDefault();
2343 this->writeEmulatedSwitchFallthroughCases(fallthroughCases, valueExpr);
2344 fallthroughCases.
clear();
2348 previousCaseFellThrough =
false;
2352 foundNativeDefault |= sc.isDefault();
2354 this->writeSwitchCases(nativeCases);
2355 nativeCases.
clear();
2364 nativeCases.
clear();
2367 foundFallthroughDefault |= sc.isDefault();
2368 previousCaseFellThrough =
true;
2372 this->writeSwitchCases(nativeCases);
2373 nativeCases.
clear();
2375 this->writeEmulatedSwitchFallthroughCases(fallthroughCases, valueExpr);
2376 fallthroughCases.
clear();
2379 if (!foundNativeDefault && !foundFallthroughDefault) {
2380 this->writeLine(
"case default {}");
2384 this->writeLine(
"}");
2387void WGSLCodeGenerator::writeVarDeclaration(
const VarDeclaration& varDecl) {
2388 std::string initialValue =
2389 varDecl.value() ? this->assembleExpression(*varDecl.value(), Precedence::kAssignment)
2392 if (varDecl.var()->modifierFlags().isConst() ||
2393 (
fProgram.
fUsage->get(*varDecl.var()).fWrite == 1 && varDecl.value())) {
2395 SkASSERTF(varDecl.value(),
"an immutable variable must specify a value");
2396 const bool useConst =
2398 this->write(useConst ?
"const " :
"let ");
2400 this->write(
"var ");
2402 this->write(this->assembleName(varDecl.var()->mangledName()));
2404 this->write(to_wgsl_type(
fContext, varDecl.var()->type(), &varDecl.var()->layout()));
2406 if (varDecl.value()) {
2408 this->write(initialValue);
2414std::unique_ptr<WGSLCodeGenerator::LValue> WGSLCodeGenerator::makeLValue(
const Expression&
e) {
2415 if (
e.is<VariableReference>()) {
2416 return std::make_unique<PointerLValue>(
2417 this->variableReferenceNameForLValue(
e.as<VariableReference>()));
2419 if (
e.is<FieldAccess>()) {
2420 return std::make_unique<PointerLValue>(this->assembleFieldAccess(
e.as<FieldAccess>()));
2422 if (
e.is<IndexExpression>()) {
2423 const IndexExpression& idx =
e.as<IndexExpression>();
2424 if (idx.base()->type().isVector()) {
2426 if (std::unique_ptr<Expression> rewrite =
2428 return std::make_unique<VectorComponentLValue>(
2429 this->assembleExpression(*rewrite, Precedence::kAssignment));
2431 return std::make_unique<VectorComponentLValue>(this->assembleIndexExpression(idx));
2434 return std::make_unique<PointerLValue>(this->assembleIndexExpression(idx));
2437 if (
e.is<Swizzle>()) {
2438 const Swizzle& swizzle =
e.as<Swizzle>();
2439 if (swizzle.components().size() == 1) {
2440 return std::make_unique<VectorComponentLValue>(this->assembleSwizzle(swizzle));
2442 return std::make_unique<SwizzleLValue>(
2444 this->assembleExpression(*swizzle.base(), Precedence::kAssignment),
2445 swizzle.base()->type(),
2446 swizzle.components());
2454std::string WGSLCodeGenerator::assembleExpression(
const Expression&
e,
2455 Precedence parentPrecedence) {
2457 case Expression::Kind::kBinary:
2458 return this->assembleBinaryExpression(
e.as<BinaryExpression>(), parentPrecedence);
2460 case Expression::Kind::kConstructorCompound:
2461 return this->assembleConstructorCompound(
e.as<ConstructorCompound>());
2463 case Expression::Kind::kConstructorArrayCast:
2466 return this->assembleExpression(*
e.as<ConstructorArrayCast>().argument(),
2469 case Expression::Kind::kConstructorArray:
2470 case Expression::Kind::kConstructorCompoundCast:
2471 case Expression::Kind::kConstructorScalarCast:
2472 case Expression::Kind::kConstructorSplat:
2473 case Expression::Kind::kConstructorStruct:
2474 return this->assembleAnyConstructor(
e.asAnyConstructor());
2476 case Expression::Kind::kConstructorDiagonalMatrix:
2477 return this->assembleConstructorDiagonalMatrix(
e.as<ConstructorDiagonalMatrix>());
2479 case Expression::Kind::kConstructorMatrixResize:
2480 return this->assembleConstructorMatrixResize(
e.as<ConstructorMatrixResize>());
2485 case Expression::Kind::kFieldAccess:
2486 return this->assembleFieldAccess(
e.as<FieldAccess>());
2488 case Expression::Kind::kFunctionCall:
2489 return this->assembleFunctionCall(
e.as<FunctionCall>(), parentPrecedence);
2492 return this->assembleIndexExpression(
e.as<IndexExpression>());
2495 return this->assembleLiteral(
e.as<Literal>());
2497 case Expression::Kind::kPrefix:
2498 return this->assemblePrefixExpression(
e.as<PrefixExpression>(), parentPrecedence);
2500 case Expression::Kind::kPostfix:
2501 return this->assemblePostfixExpression(
e.as<PostfixExpression>(), parentPrecedence);
2503 case Expression::Kind::kSetting:
2504 return this->assembleExpression(*
e.as<Setting>().toLiteral(
fCaps), parentPrecedence);
2506 case Expression::Kind::kSwizzle:
2507 return this->assembleSwizzle(
e.as<Swizzle>());
2509 case Expression::Kind::kTernary:
2510 return this->assembleTernaryExpression(
e.as<TernaryExpression>(), parentPrecedence);
2512 case Expression::Kind::kVariableReference:
2513 return this->assembleVariableReference(
e.as<VariableReference>());
2516 SkDEBUGFAILF(
"unsupported expression:\n%s",
e.description().c_str());
2539 switch (op.
kind()) {
2558bool WGSLCodeGenerator::binaryOpNeedsComponentwiseMatrixPolyfill(
const Type& left,
2561 switch (op.kind()) {
2564 if (left.isMatrix() && right.isMatrix()) {
2573 return (
left.isMatrix() &&
right.isScalar()) ||
2581std::string WGSLCodeGenerator::assembleBinaryExpression(
const BinaryExpression&
b,
2582 Precedence parentPrecedence) {
2583 return this->assembleBinaryExpression(*
b.left(),
b.getOperator(), *
b.right(),
b.type(),
2587std::string WGSLCodeGenerator::assembleBinaryExpression(
const Expression& left,
2589 const Expression& right,
2590 const Type& resultType,
2591 Precedence parentPrecedence) {
2613 expr = this->writeScratchVar(resultType);
2615 std::string leftExpr = this->assembleExpression(left, Precedence::kExpression);
2617 this->write(leftExpr);
2618 this->writeLine(
" {");
2621 std::string rightExpr = this->assembleExpression(right, Precedence::kAssignment);
2624 this->write(rightExpr);
2625 this->writeLine(
";");
2628 this->writeLine(
"} else {");
2632 this->writeLine(
" = false;");
2635 this->writeLine(
"}");
2651 expr = this->writeScratchVar(resultType);
2653 std::string leftExpr = this->assembleExpression(left, Precedence::kExpression);
2655 this->write(leftExpr);
2656 this->writeLine(
" {");
2660 this->writeLine(
" = true;");
2663 this->writeLine(
"} else {");
2666 std::string rightExpr = this->assembleExpression(right, Precedence::kAssignment);
2669 this->write(rightExpr);
2670 this->writeLine(
";");
2673 this->writeLine(
"}");
2680 this->assembleExpression(left, Precedence::kStatement);
2683 return this->assembleExpression(right, parentPrecedence);
2687 if (op.isAssignment()) {
2688 std::unique_ptr<LValue> lvalue = this->makeLValue(left);
2695 expr = this->assembleExpression(right, Precedence::kAssignment);
2698 op = op.removeAssignment();
2700 std::string lhs = lvalue->load();
2701 std::string rhs = this->assembleExpression(right, op.getBinaryPrecedence());
2703 if (this->binaryOpNeedsComponentwiseMatrixPolyfill(
left.type(),
right.type(), op)) {
2705 rhs = this->writeScratchLet(rhs);
2708 expr = this->assembleComponentwiseMatrixBinary(
left.type(),
right.type(),
2716 this->writeLine(lvalue->store(expr));
2719 return lvalue->load();
2722 if (op.isEquality()) {
2723 return this->assembleEqualityExpression(left, right, op, parentPrecedence);
2726 Precedence precedence = op.getBinaryPrecedence();
2727 bool needParens = precedence >= parentPrecedence;
2729 precedence = Precedence::kParentheses;
2744 std::string lhs = this->assembleExpression(left, precedence);
2745 std::string rhs = this->assembleExpression(right, precedence);
2747 if (this->binaryOpNeedsComponentwiseMatrixPolyfill(
left.type(),
right.type(), op)) {
2749 lhs = this->writeScratchLet(lhs);
2752 rhs = this->writeScratchLet(rhs);
2755 expr += this->assembleComponentwiseMatrixBinary(
left.type(),
right.type(), lhs, rhs, op);
2757 if (bothSidesConstant) {
2758 lhs = this->writeScratchLet(lhs);
2771std::string WGSLCodeGenerator::assembleFieldAccess(
const FieldAccess&
f) {
2772 const Field* field = &
f.base()->type().fields()[
f.fieldIndex()];
2775 if (FieldPolyfillInfo* polyfillInfo = fFieldPolyfillMap.find(field)) {
2780 polyfillInfo->fWasAccessed =
true;
2784 const Expression*
base =
f.base().get();
2785 const IndexExpression* indexExpr =
nullptr;
2786 if (
base->is<IndexExpression>()) {
2787 indexExpr = &
base->as<IndexExpression>();
2788 base = indexExpr->base().get();
2792 expr = polyfillInfo->fReplacementName;
2797 expr += this->assembleExpression(*indexExpr->index(), Precedence::kSequence);
2803 switch (
f.ownerKind()) {
2805 expr = this->assembleExpression(*
f.base(), Precedence::kPostfix) +
'.';
2808 case FieldAccess::OwnerKind::kAnonymousInterfaceBlock:
2809 if (
f.base()->is<VariableReference>() &&
2811 expr = this->variablePrefix(*
f.base()->as<VariableReference>().variable());
2816 expr += this->assembleName(field->fName);
2827 for (
const std::unique_ptr<Expression>& arg : arguments) {
2835std::string WGSLCodeGenerator::assembleSimpleIntrinsic(std::string_view intrinsicName,
2836 const FunctionCall&
call) {
2838 std::string expr = std::string(intrinsicName);
2839 expr.push_back(
'(');
2840 const ExpressionArray&
args =
call.arguments();
2843 for (
int index = 0; index <
args.size(); ++index) {
2844 expr += separator();
2846 std::string argument = this->assembleExpression(*
args[index], Precedence::kSequence);
2847 if (
args[index]->
type().isAtomic()) {
2851 }
else if (allConstant && index == 0) {
2853 expr += this->writeScratchLet(argument);
2858 expr.push_back(
')');
2860 if (
call.type().isVoid()) {
2862 this->writeLine(
";");
2863 return std::string();
2865 return this->writeScratchLet(expr);
2869std::string WGSLCodeGenerator::assembleVectorizedIntrinsic(std::string_view intrinsicName,
2870 const FunctionCall&
call) {
2874 std::string expr = std::string(intrinsicName);
2875 expr.push_back(
'(');
2878 const ExpressionArray&
args =
call.arguments();
2879 bool returnsVector =
call.type().isVector();
2881 for (
int index = 0; index <
args.size(); ++index) {
2882 expr += separator();
2884 bool vectorize = returnsVector &&
args[index]->type().isScalar();
2887 expr.push_back(
'(');
2891 std::string argument = this->assembleExpression(*
args[index], Precedence::kSequence);
2892 expr += (allConstant && index == 0) ? this->writeScratchLet(argument)
2895 expr.push_back(
')');
2898 expr.push_back(
')');
2900 return this->writeScratchLet(expr);
2903std::string WGSLCodeGenerator::assembleUnaryOpIntrinsic(Operator op,
2904 const FunctionCall&
call,
2905 Precedence parentPrecedence) {
2908 bool needParens = Precedence::kPrefix >= parentPrecedence;
2912 expr.push_back(
'(');
2916 expr += this->assembleExpression(*
call.arguments()[0], Precedence::kPrefix);
2919 expr.push_back(
')');
2925std::string WGSLCodeGenerator::assembleBinaryOpIntrinsic(Operator op,
2926 const FunctionCall&
call,
2927 Precedence parentPrecedence) {
2930 Precedence precedence = op.getBinaryPrecedence();
2931 bool needParens = precedence >= parentPrecedence ||
2935 expr.push_back(
'(');
2939 std::string argument = this->assembleExpression(*
call.arguments()[0], precedence);
2943 expr += this->assembleExpression(*
call.arguments()[1], precedence);
2946 expr.push_back(
')');
2955std::string WGSLCodeGenerator::assembleOutAssignedIntrinsic(std::string_view intrinsicName,
2956 std::string_view returnField,
2957 std::string_view outField,
2958 const FunctionCall&
call) {
2963 std::string expr = std::string(intrinsicName);
2968 std::string argument = this->assembleExpression(*
call.arguments()[0], Precedence::kSequence);
2970 ? this->writeScratchLet(argument) : argument;
2974 expr = this->writeScratchLet(expr);
2978 std::unique_ptr<LValue> lvalue = this->makeLValue(*
call.arguments()[1]);
2982 std::string outValue = expr;
2983 outValue += outField;
2984 this->writeLine(lvalue->store(outValue));
2987 expr += returnField;
2991std::string WGSLCodeGenerator::assemblePartialSampleCall(std::string_view functionName,
2992 const Expression& sampler,
2993 const Expression& coords) {
2998 std::string expr = std::string(functionName) +
'(';
2999 expr += this->assembleExpression(sampler, Precedence::kSequence);
3000 expr += kTextureSuffix;
3002 expr += this->assembleExpression(sampler, Precedence::kSequence);
3003 expr += kSamplerSuffix;
3007 SkASSERT(coords.type().isVector());
3008 if (coords.type().columns() == 3) {
3010 std::string vec3Coords = this->writeScratchLet(coords, Precedence::kMultiplicative);
3011 expr += vec3Coords +
".xy / " + vec3Coords +
".z";
3014 SkASSERT(coords.type().columns() == 2);
3015 expr += this->assembleExpression(coords, Precedence::kSequence);
3021std::string WGSLCodeGenerator::assembleComponentwiseMatrixBinary(
const Type& leftType,
3022 const Type& rightType,
3023 const std::string& left,
3024 const std::string& right,
3026 bool leftIsMatrix = leftType.isMatrix();
3027 bool rightIsMatrix = rightType.isMatrix();
3028 const Type& matrixType = leftIsMatrix ? leftType : rightType;
3030 std::string expr = to_wgsl_type(
fContext, matrixType) +
'(';
3032 int columns = matrixType.columns();
3033 for (
int c = 0; c < columns; ++c) {
3034 expr += separator();
3041 expr += op.operatorName();
3043 if (rightIsMatrix) {
3052std::string WGSLCodeGenerator::assembleIntrinsicCall(
const FunctionCall&
call,
3054 Precedence parentPrecedence) {
3061 const ExpressionArray& arguments =
call.arguments();
3063 case k_atan_IntrinsicKind: {
3064 const char*
name = (arguments.size() == 1) ?
"atan" :
"atan2";
3065 return this->assembleSimpleIntrinsic(
name,
call);
3067 case k_dFdx_IntrinsicKind:
3068 return this->assembleSimpleIntrinsic(
"dpdx",
call);
3070 case k_dFdy_IntrinsicKind:
3072 return this->assembleSimpleIntrinsic(
"dpdy",
call);
3074 case k_dot_IntrinsicKind: {
3075 if (arguments[0]->
type().isScalar()) {
3078 return this->assembleSimpleIntrinsic(
"dot",
call);
3080 case k_equal_IntrinsicKind:
3083 case k_faceforward_IntrinsicKind: {
3084 if (arguments[0]->
type().isScalar()) {
3086 std::string
N = this->writeNontrivialScratchLet(*arguments[0],
3087 Precedence::kAssignment);
3088 return this->writeScratchLet(
3089 "select(-" +
N +
", " +
N +
", " +
3090 this->assembleBinaryExpression(*arguments[1],
3093 arguments[1]->
type(),
3094 Precedence::kRelational) +
3097 return this->assembleSimpleIntrinsic(
"faceForward",
call);
3099 case k_frexp_IntrinsicKind:
3102 return this->assembleOutAssignedIntrinsic(
"frexp",
"fract",
"exp",
call);
3104 case k_greaterThan_IntrinsicKind:
3107 case k_greaterThanEqual_IntrinsicKind:
3110 case k_inverse_IntrinsicKind:
3111 return this->assembleInversePolyfill(
call);
3113 case k_inversesqrt_IntrinsicKind:
3114 return this->assembleSimpleIntrinsic(
"inverseSqrt",
call);
3116 case k_lessThan_IntrinsicKind:
3119 case k_lessThanEqual_IntrinsicKind:
3122 case k_matrixCompMult_IntrinsicKind: {
3125 ? this->writeScratchLet(*arguments[0], Precedence::kPostfix)
3126 : this->writeNontrivialScratchLet(*arguments[0], Precedence::
kPostfix);
3127 std::string arg1 = this->writeNontrivialScratchLet(*arguments[1], Precedence::kPostfix);
3128 return this->writeScratchLet(
3129 this->assembleComponentwiseMatrixBinary(arguments[0]->
type(),
3130 arguments[1]->
type(),
3135 case k_mix_IntrinsicKind: {
3136 const char*
name = arguments[2]->type().componentType().isBoolean() ?
"select" :
"mix";
3137 return this->assembleVectorizedIntrinsic(
name,
call);
3139 case k_mod_IntrinsicKind: {
3144 ? this->writeScratchLet(*arguments[0], Precedence::kAdditive)
3145 : this->writeNontrivialScratchLet(*arguments[0], Precedence::
kAdditive);
3146 std::string arg1 = this->writeNontrivialScratchLet(*arguments[1],
3147 Precedence::kAdditive);
3148 return this->writeScratchLet(arg0 +
" - " + arg1 +
" * floor(" +
3149 arg0 +
" / " + arg1 +
")");
3152 case k_modf_IntrinsicKind:
3155 return this->assembleOutAssignedIntrinsic(
"modf",
"fract",
"whole",
call);
3157 case k_normalize_IntrinsicKind: {
3158 const char*
name = arguments[0]->type().isScalar() ?
"sign" :
"normalize";
3159 return this->assembleSimpleIntrinsic(
name,
call);
3161 case k_not_IntrinsicKind:
3164 case k_notEqual_IntrinsicKind:
3167 case k_packHalf2x16_IntrinsicKind:
3168 return this->assembleSimpleIntrinsic(
"pack2x16float",
call);
3170 case k_packSnorm2x16_IntrinsicKind:
3171 return this->assembleSimpleIntrinsic(
"pack2x16snorm",
call);
3173 case k_packSnorm4x8_IntrinsicKind:
3174 return this->assembleSimpleIntrinsic(
"pack4x8snorm",
call);
3176 case k_packUnorm2x16_IntrinsicKind:
3177 return this->assembleSimpleIntrinsic(
"pack2x16unorm",
call);
3179 case k_packUnorm4x8_IntrinsicKind:
3180 return this->assembleSimpleIntrinsic(
"pack4x8unorm",
call);
3182 case k_reflect_IntrinsicKind:
3183 if (arguments[0]->
type().isScalar()) {
3186 std::string
I = this->writeNontrivialScratchLet(*arguments[0],
3187 Precedence::kAdditive);
3189 ? this->writeScratchLet(*arguments[1], Precedence::kMultiplicative)
3190 : this->writeNontrivialScratchLet(*arguments[1], Precedence::
kMultiplicative);
3191 return this->writeScratchLet(
String::printf(
"%s - 2 * %s * %s * %s",
3192 I.c_str(),
N.c_str(),
3193 I.c_str(),
N.c_str()));
3195 return this->assembleSimpleIntrinsic(
"reflect",
call);
3197 case k_refract_IntrinsicKind:
3198 if (arguments[0]->
type().isScalar()) {
3201 std::string
I = this->writeNontrivialScratchLet(*arguments[0],
3202 Precedence::kSequence);
3203 std::string
N = this->writeNontrivialScratchLet(*arguments[1],
3204 Precedence::kSequence);
3207 ? this->writeScratchLet(*arguments[2], Precedence::kSequence)
3208 : this->writeNontrivialScratchLet(*arguments[2], Precedence::
kSequence);
3209 return this->writeScratchLet(
3210 String::printf(
"refract(vec2<%s>(%s, 0), vec2<%s>(%s, 0), %s).x",
3217 return this->assembleSimpleIntrinsic(
"refract",
call);
3219 case k_sample_IntrinsicKind: {
3221 SkASSERT(arguments.size() == 2 || arguments.size() == 3);
3222 bool callIncludesBias = (arguments.size() == 3);
3224 if (
fProgram.
fConfig->fSettings.fSharpenTextures || callIncludesBias) {
3226 std::string expr = this->assemblePartialSampleCall(
"textureSampleBias",
3230 if (callIncludesBias) {
3231 expr += this->assembleExpression(*arguments[2], Precedence::kAdditive) +
3241 return this->assemblePartialSampleCall(
"textureSample",
3243 *arguments[1]) +
')';
3245 case k_sampleLod_IntrinsicKind: {
3246 std::string expr = this->assemblePartialSampleCall(
"textureSampleLevel",
3249 expr +=
", " + this->assembleExpression(*arguments[2], Precedence::kSequence);
3252 case k_sampleGrad_IntrinsicKind: {
3253 std::string expr = this->assemblePartialSampleCall(
"textureSampleGrad",
3256 expr +=
", " + this->assembleExpression(*arguments[2], Precedence::kSequence);
3257 expr +=
", " + this->assembleExpression(*arguments[3], Precedence::kSequence);
3260 case k_textureHeight_IntrinsicKind:
3261 return this->assembleSimpleIntrinsic(
"textureDimensions",
call) +
".y";
3263 case k_textureRead_IntrinsicKind: {
3266 std::string tex = this->assembleExpression(*arguments[0], Precedence::kSequence);
3267 std::string
pos = this->writeScratchLet(*arguments[1], Precedence::kSequence);
3268 return std::string(
"textureLoad(") + tex +
", " +
pos +
", 0)";
3270 case k_textureWidth_IntrinsicKind:
3271 return this->assembleSimpleIntrinsic(
"textureDimensions",
call) +
".x";
3273 case k_textureWrite_IntrinsicKind:
3274 return this->assembleSimpleIntrinsic(
"textureStore",
call);
3276 case k_unpackHalf2x16_IntrinsicKind:
3277 return this->assembleSimpleIntrinsic(
"unpack2x16float",
call);
3279 case k_unpackSnorm2x16_IntrinsicKind:
3280 return this->assembleSimpleIntrinsic(
"unpack2x16snorm",
call);
3282 case k_unpackSnorm4x8_IntrinsicKind:
3283 return this->assembleSimpleIntrinsic(
"unpack4x8snorm",
call);
3285 case k_unpackUnorm2x16_IntrinsicKind:
3286 return this->assembleSimpleIntrinsic(
"unpack2x16unorm",
call);
3288 case k_unpackUnorm4x8_IntrinsicKind:
3289 return this->assembleSimpleIntrinsic(
"unpack4x8unorm",
call);
3291 case k_loadFloatBuffer_IntrinsicKind: {
3296 call.arguments()[0]->clone());
3298 return this->assembleExpression(*indexExpression, Precedence::kExpression);
3301 case k_clamp_IntrinsicKind:
3302 case k_max_IntrinsicKind:
3303 case k_min_IntrinsicKind:
3304 case k_smoothstep_IntrinsicKind:
3305 case k_step_IntrinsicKind:
3306 return this->assembleVectorizedIntrinsic(
call.function().name(),
call);
3308 case k_abs_IntrinsicKind:
3309 case k_acos_IntrinsicKind:
3310 case k_all_IntrinsicKind:
3311 case k_any_IntrinsicKind:
3312 case k_asin_IntrinsicKind:
3313 case k_atomicAdd_IntrinsicKind:
3314 case k_atomicLoad_IntrinsicKind:
3315 case k_atomicStore_IntrinsicKind:
3316 case k_ceil_IntrinsicKind:
3317 case k_cos_IntrinsicKind:
3318 case k_cross_IntrinsicKind:
3319 case k_degrees_IntrinsicKind:
3320 case k_distance_IntrinsicKind:
3321 case k_exp_IntrinsicKind:
3322 case k_exp2_IntrinsicKind:
3323 case k_floor_IntrinsicKind:
3324 case k_fract_IntrinsicKind:
3325 case k_length_IntrinsicKind:
3326 case k_log_IntrinsicKind:
3327 case k_log2_IntrinsicKind:
3328 case k_radians_IntrinsicKind:
3329 case k_pow_IntrinsicKind:
3330 case k_saturate_IntrinsicKind:
3331 case k_sign_IntrinsicKind:
3332 case k_sin_IntrinsicKind:
3333 case k_sqrt_IntrinsicKind:
3334 case k_storageBarrier_IntrinsicKind:
3335 case k_tan_IntrinsicKind:
3336 case k_workgroupBarrier_IntrinsicKind:
3338 return this->assembleSimpleIntrinsic(
call.function().name(),
call);
3343 "fn mat2_inverse(m: mat2x2<f32>) -> mat2x2<f32> {"
3344"\n" "return mat2x2<f32>(m[1].y, -m[0].y, -m[1].x, m[0].x) * (1/determinant(m));"
3349 "fn mat3_inverse(m: mat3x3<f32>) -> mat3x3<f32> {"
3350"\n" "let a00 = m[0].x; let a01 = m[0].y; let a02 = m[0].z;"
3351"\n" "let a10 = m[1].x; let a11 = m[1].y; let a12 = m[1].z;"
3352"\n" "let a20 = m[2].x; let a21 = m[2].y; let a22 = m[2].z;"
3353"\n" "let b01 = a22*a11 - a12*a21;"
3354"\n" "let b11 = -a22*a10 + a12*a20;"
3355"\n" "let b21 = a21*a10 - a11*a20;"
3356"\n" "let det = a00*b01 + a01*b11 + a02*b21;"
3357"\n" "return mat3x3<f32>(b01, (-a22*a01 + a02*a21), ( a12*a01 - a02*a11),"
3358"\n" "b11, ( a22*a00 - a02*a20), (-a12*a00 + a02*a10),"
3359"\n" "b21, (-a21*a00 + a01*a20), ( a11*a00 - a01*a10)) * (1/det);"
3364 "fn mat4_inverse(m: mat4x4<f32>) -> mat4x4<f32>{"
3365"\n" "let a00 = m[0].x; let a01 = m[0].y; let a02 = m[0].z; let a03 = m[0].w;"
3366"\n" "let a10 = m[1].x; let a11 = m[1].y; let a12 = m[1].z; let a13 = m[1].w;"
3367"\n" "let a20 = m[2].x; let a21 = m[2].y; let a22 = m[2].z; let a23 = m[2].w;"
3368"\n" "let a30 = m[3].x; let a31 = m[3].y; let a32 = m[3].z; let a33 = m[3].w;"
3369"\n" "let b00 = a00*a11 - a01*a10;"
3370"\n" "let b01 = a00*a12 - a02*a10;"
3371"\n" "let b02 = a00*a13 - a03*a10;"
3372"\n" "let b03 = a01*a12 - a02*a11;"
3373"\n" "let b04 = a01*a13 - a03*a11;"
3374"\n" "let b05 = a02*a13 - a03*a12;"
3375"\n" "let b06 = a20*a31 - a21*a30;"
3376"\n" "let b07 = a20*a32 - a22*a30;"
3377"\n" "let b08 = a20*a33 - a23*a30;"
3378"\n" "let b09 = a21*a32 - a22*a31;"
3379"\n" "let b10 = a21*a33 - a23*a31;"
3380"\n" "let b11 = a22*a33 - a23*a32;"
3381"\n" "let det = b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06;"
3382"\n" "return mat4x4<f32>(a11*b11 - a12*b10 + a13*b09,"
3383"\n" "a02*b10 - a01*b11 - a03*b09,"
3384"\n" "a31*b05 - a32*b04 + a33*b03,"
3385"\n" "a22*b04 - a21*b05 - a23*b03,"
3386"\n" "a12*b08 - a10*b11 - a13*b07,"
3387"\n" "a00*b11 - a02*b08 + a03*b07,"
3388"\n" "a32*b02 - a30*b05 - a33*b01,"
3389"\n" "a20*b05 - a22*b02 + a23*b01,"
3390"\n" "a10*b10 - a11*b08 + a13*b06,"
3391"\n" "a01*b08 - a00*b10 - a03*b06,"
3392"\n" "a30*b04 - a31*b02 + a33*b00,"
3393"\n" "a21*b02 - a20*b04 - a23*b00,"
3394"\n" "a11*b07 - a10*b09 - a12*b06,"
3395"\n" "a00*b09 - a01*b07 + a02*b06,"
3396"\n" "a31*b01 - a30*b03 - a32*b00,"
3397"\n" "a20*b03 - a21*b01 + a22*b00) * (1/det);"
3401std::string WGSLCodeGenerator::assembleInversePolyfill(
const FunctionCall&
call) {
3412 switch (
type.slotCount()) {
3414 if (!fWrittenInverse2) {
3415 fWrittenInverse2 =
true;
3418 return this->assembleSimpleIntrinsic(
"mat2_inverse",
call);
3421 if (!fWrittenInverse3) {
3422 fWrittenInverse3 =
true;
3425 return this->assembleSimpleIntrinsic(
"mat3_inverse",
call);
3428 if (!fWrittenInverse4) {
3429 fWrittenInverse4 =
true;
3432 return this->assembleSimpleIntrinsic(
"mat4_inverse",
call);
3440std::string WGSLCodeGenerator::assembleFunctionCall(
const FunctionCall&
call,
3441 Precedence parentPrecedence) {
3442 const FunctionDeclaration& func =
call.function();
3446 if (func.isIntrinsic()) {
3447 return this->assembleIntrinsicCall(
call, func.intrinsicKind(), parentPrecedence);
3463 const ExpressionArray&
args =
call.arguments();
3472 for (
int index = 0; index <
args.size(); ++index) {
3474 std::unique_ptr<LValue> lvalue = this->makeLValue(*
args[index]);
3485 substituteArgument.
push_back(std::string());
3490 std::string expr = this->assembleName(func.mangledName());
3491 expr.push_back(
'(');
3494 if (std::string funcDepArgs = this->functionDependencyArgs(func); !funcDepArgs.empty()) {
3495 expr += funcDepArgs;
3500 for (
int index = 0; index <
args.size(); ++index) {
3501 expr += separator();
3502 if (!substituteArgument[index].
empty()) {
3504 expr +=
'&' + substituteArgument[index];
3505 }
else if (
args[index]->
type().isSampler()) {
3509 expr += this->assembleExpression(*
args[index], Precedence::kSequence);
3510 expr += kTextureSuffix;
3512 expr += this->assembleExpression(*
args[index], Precedence::kSequence);
3513 expr += kSamplerSuffix;
3515 expr += this->assembleExpression(*
args[index], Precedence::kSequence);
3520 if (
call.type().isVoid()) {
3524 SkASSERT(parentPrecedence >= Precedence::kSequence);
3526 this->writeLine(
";");
3528 result = this->writeScratchLet(expr);
3532 for (
int index = 0; index <
args.size(); ++index) {
3533 if (!substituteArgument[index].
empty()) {
3534 this->writeLine(writeback[index]->
store(substituteArgument[index]));
3542std::string WGSLCodeGenerator::assembleIndexExpression(
const IndexExpression&
i) {
3544 std::string idx = this->writeNontrivialScratchLet(*
i.index(), Precedence::kExpression);
3545 return this->assembleExpression(*
i.base(), Precedence::kPostfix) +
"[" + idx +
"]";
3548std::string WGSLCodeGenerator::assembleLiteral(
const Literal& l) {
3550 if (
type.isFloat() ||
type.isBoolean()) {
3563std::string WGSLCodeGenerator::assembleIncrementExpr(
const Type&
type) {
3566 expr.push_back(
'(');
3570 for (
int slots =
type.slotCount(); slots > 0; --slots) {
3571 expr += separator();
3574 expr.push_back(
')');
3578std::string WGSLCodeGenerator::assemblePrefixExpression(
const PrefixExpression&
p,
3579 Precedence parentPrecedence) {
3581 Operator op =
p.getOperator();
3583 return this->assembleExpression(*
p.operand(), Precedence::kPrefix);
3587 if (op.kind() == Operator::Kind::PLUSPLUS || op.kind() == Operator::Kind::MINUSMINUS) {
3588 std::unique_ptr<LValue> lvalue = this->makeLValue(*
p.operand());
3594 std::string newValue =
3596 (
p.getOperator().kind() == Operator::Kind::PLUSPLUS ?
" + " :
" - ") +
3597 this->assembleIncrementExpr(
p.operand()->type());
3598 this->writeLine(lvalue->store(newValue));
3599 return lvalue->load();
3610 const bool needsNegation = op.kind() == Operator::Kind::MINUS &&
3611 !
p.operand()->type().isScalar() && !
p.operand()->type().isVector();
3612 const bool needParens = needsNegation || Precedence::kPrefix >= parentPrecedence;
3615 expr.push_back(
'(');
3618 if (needsNegation) {
3620 expr += this->assembleExpression(*
p.operand(), Precedence::kMultiplicative);
3622 expr +=
p.getOperator().tightOperatorName();
3623 expr += this->assembleExpression(*
p.operand(), Precedence::kPrefix);
3627 expr.push_back(
')');
3633std::string WGSLCodeGenerator::assemblePostfixExpression(
const PostfixExpression&
p,
3634 Precedence parentPrecedence) {
3635 SkASSERT(
p.getOperator().kind() == Operator::Kind::PLUSPLUS ||
3636 p.getOperator().kind() == Operator::Kind::MINUSMINUS);
3640 std::unique_ptr<LValue> lvalue = this->makeLValue(*
p.operand());
3647 std::string originalValue;
3648 if (parentPrecedence != Precedence::kStatement) {
3649 originalValue = this->writeScratchLet(lvalue->load());
3652 std::string newValue = lvalue->load() +
3653 (
p.getOperator().kind() == Operator::Kind::PLUSPLUS ?
" + " :
" - ") +
3654 this->assembleIncrementExpr(
p.operand()->type());
3655 this->writeLine(lvalue->store(newValue));
3657 return originalValue;
3660std::string WGSLCodeGenerator::assembleSwizzle(
const Swizzle& swizzle) {
3661 return this->assembleExpression(*swizzle.base(), Precedence::kPostfix) +
"." +
3665std::string WGSLCodeGenerator::writeScratchVar(
const Type&
type,
const std::string&
value) {
3666 std::string scratchVarName =
"_skTemp" +
std::to_string(fScratchCount++);
3667 this->write(
"var ");
3668 this->write(scratchVarName);
3671 if (!
value.empty()) {
3675 this->writeLine(
";");
3676 return scratchVarName;
3679std::string WGSLCodeGenerator::writeScratchLet(
const std::string& expr,
3680 bool isCompileTimeConstant) {
3681 std::string scratchVarName =
"_skTemp" +
std::to_string(fScratchCount++);
3682 this->write(fAtFunctionScope && !isCompileTimeConstant ?
"let " :
"const ");
3683 this->write(scratchVarName);
3686 this->writeLine(
";");
3687 return scratchVarName;
3690std::string WGSLCodeGenerator::writeScratchLet(
const Expression& expr,
3691 Precedence parentPrecedence) {
3692 return this->writeScratchLet(this->assembleExpression(expr, parentPrecedence));
3695std::string WGSLCodeGenerator::writeNontrivialScratchLet(
const Expression& expr,
3696 Precedence parentPrecedence) {
3697 std::string
result = this->assembleExpression(expr, parentPrecedence);
3703std::string WGSLCodeGenerator::assembleTernaryExpression(
const TernaryExpression& t,
3704 Precedence parentPrecedence) {
3711 if ((t.type().isScalar() || t.type().isVector()) &&
3716 bool needParens = Precedence::kTernary >= parentPrecedence;
3718 expr.push_back(
'(');
3721 expr += this->assembleExpression(*t.ifFalse(), Precedence::kSequence);
3723 expr += this->assembleExpression(*t.ifTrue(), Precedence::kSequence);
3726 bool isVector = t.type().isVector();
3731 expr += this->assembleExpression(*t.test(), Precedence::kSequence);
3733 expr.push_back(
')');
3735 expr.push_back(
')');
3737 expr.push_back(
')');
3743 expr = this->writeScratchVar(t.ifTrue()->type());
3745 std::string testExpr = this->assembleExpression(*t.test(), Precedence::kExpression);
3747 this->write(testExpr);
3748 this->writeLine(
" {");
3751 std::string trueExpr = this->assembleExpression(*t.ifTrue(), Precedence::kAssignment);
3754 this->write(trueExpr);
3755 this->writeLine(
";");
3758 this->writeLine(
"} else {");
3761 std::string falseExpr = this->assembleExpression(*t.ifFalse(), Precedence::kAssignment);
3764 this->write(falseExpr);
3765 this->writeLine(
";");
3768 this->writeLine(
"}");
3773std::string WGSLCodeGenerator::variablePrefix(
const Variable& v) {
3783 return "(*_stageOut).";
3788 if (
const InterfaceBlock* ib = v.interfaceBlock()) {
3789 const Type& ibType = ib->var()->type().componentType();
3790 if (
const std::string* ibName = fInterfaceBlockNameMap.find(&ibType)) {
3791 return *ibName +
'.';
3798 if (is_in_global_uniforms(v)) {
3799 return "_globalUniforms.";
3806std::string WGSLCodeGenerator::variableReferenceNameForLValue(
const VariableReference& r) {
3807 const Variable& v = *r.variable();
3809 if ((v.storage() == Variable::Storage::kParameter &&
3813 return "(*" + this->assembleName(v.mangledName()) +
')';
3816 return this->variablePrefix(v) + this->assembleName(v.mangledName());
3819std::string WGSLCodeGenerator::assembleVariableReference(
const VariableReference& r) {
3821 const Variable& v = *r.variable();
3826 std::optional<std::string_view> conversion = needs_builtin_type_conversion(v);
3827 if (conversion.has_value()) {
3828 expr += *conversion;
3829 expr.push_back(
'(');
3832 expr += this->variableReferenceNameForLValue(r);
3834 if (conversion.has_value()) {
3835 expr.push_back(
')');
3841std::string WGSLCodeGenerator::assembleAnyConstructor(
const AnyConstructor& c) {
3842 std::string expr = to_wgsl_type(
fContext, c.type());
3843 expr.push_back(
'(');
3845 for (
const auto&
e : c.argumentSpan()) {
3846 expr += separator();
3847 expr += this->assembleExpression(*
e, Precedence::kSequence);
3849 expr.push_back(
')');
3853std::string WGSLCodeGenerator::assembleConstructorCompound(
const ConstructorCompound& c) {
3854 if (c.type().isVector()) {
3855 return this->assembleConstructorCompoundVector(c);
3856 }
else if (c.type().isMatrix()) {
3857 return this->assembleConstructorCompoundMatrix(c);
3864std::string WGSLCodeGenerator::assembleConstructorCompoundVector(
const ConstructorCompound& c) {
3869 if (c.type().columns() == 4 && c.argumentSpan().size() == 1) {
3870 const Expression& arg = *c.argumentSpan().front();
3871 if (arg.type().isMatrix()) {
3872 SkASSERT(arg.type().columns() == 2);
3875 std::string
matrix = this->writeNontrivialScratchLet(arg, Precedence::kPostfix);
3881 return this->assembleAnyConstructor(c);
3884std::string WGSLCodeGenerator::assembleConstructorCompoundMatrix(
const ConstructorCompound& ctor) {
3887 std::string expr = to_wgsl_type(
fContext, ctor.type()) +
'(';
3889 for (
const std::unique_ptr<Expression>& arg : ctor.arguments()) {
3890 SkASSERT(arg->type().isScalar() || arg->type().isVector());
3892 if (arg->type().isScalar()) {
3893 expr += separator();
3894 expr += this->assembleExpression(*arg, Precedence::kSequence);
3896 std::string inner = this->writeNontrivialScratchLet(*arg, Precedence::kSequence);
3897 int numSlots = arg->type().slotCount();
3898 for (
int slot = 0; slot < numSlots; ++slot) {
3899 String::appendf(&expr,
"%s%s[%d]", separator().c_str(), inner.c_str(), slot);
3906std::string WGSLCodeGenerator::assembleConstructorDiagonalMatrix(
3907 const ConstructorDiagonalMatrix& c) {
3910 SkASSERT(c.argument()->type().isScalar());
3913 std::string inner = this->writeNontrivialScratchLet(*c.argument(), Precedence::kAssignment);
3918 for (
int col = 0; col <
type.columns(); ++col) {
3919 for (
int row = 0; row <
type.rows(); ++row) {
3920 expr += separator();
3931std::string WGSLCodeGenerator::assembleConstructorMatrixResize(
3932 const ConstructorMatrixResize& ctor) {
3933 std::string
source = this->writeNontrivialScratchLet(*ctor.argument(), Precedence::kSequence);
3934 int columns = ctor.type().columns();
3935 int rows = ctor.type().rows();
3936 int sourceColumns = ctor.argument()->type().columns();
3937 int sourceRows = ctor.argument()->type().rows();
3939 std::string expr = to_wgsl_type(
fContext, ctor.type()) +
'(';
3941 for (
int c = 0; c < columns; ++c) {
3942 for (
int r = 0; r < rows; ++r) {
3943 expr += separator();
3944 if (c < sourceColumns && r < sourceRows) {
3946 }
else if (r == c) {
3957std::string WGSLCodeGenerator::assembleEqualityExpression(
const Type& left,
3958 const std::string& leftName,
3960 const std::string& rightName,
3962 Precedence parentPrecedence) {
3966 bool isEqual = (op.kind() == Operator::Kind::EQEQ);
3967 const char*
const combiner = isEqual ?
" && " :
" || ";
3969 if (
left.isMatrix()) {
3974 int columns =
left.columns();
3976 const char* separator =
"(";
3977 for (
int index = 0; index < columns; ++index) {
3980 expr += this->assembleEqualityExpression(vecType, leftName +
suffix,
3981 vecType, rightName +
suffix,
3982 op, Precedence::kParentheses);
3983 separator = combiner;
3988 if (
left.isArray()) {
3990 const Type& indexedType =
left.componentType();
3991 const char* separator =
"(";
3992 for (
int index = 0; index <
left.columns(); ++index) {
3995 expr += this->assembleEqualityExpression(indexedType, leftName +
suffix,
3996 indexedType, rightName +
suffix,
3997 op, Precedence::kParentheses);
3998 separator = combiner;
4003 if (
left.isStruct()) {
4008 const char* separator =
"(";
4009 for (
const Field& field : fields) {
4011 expr += this->assembleEqualityExpression(
4012 *field.fType, leftName +
'.' + this->assembleName(field.fName),
4013 *field.fType, rightName +
'.' + this->assembleName(field.fName),
4014 op, Precedence::kParentheses);
4015 separator = combiner;
4020 if (
left.isVector()) {
4025 expr += isEqual ?
"all(" :
"any(";
4034 if (parentPrecedence < Precedence::kSequence) {
4040 if (parentPrecedence < Precedence::kSequence) {
4046std::string WGSLCodeGenerator::assembleEqualityExpression(
const Expression& left,
4047 const Expression& right,
4049 Precedence parentPrecedence) {
4050 std::string leftName, rightName;
4051 if (
left.type().isScalar() ||
left.type().isVector()) {
4054 leftName = this->assembleExpression(left, Precedence::kParentheses);
4055 rightName = this->assembleExpression(right, Precedence::kParentheses);
4057 leftName = this->writeNontrivialScratchLet(left, Precedence::kAssignment);
4058 rightName = this->writeNontrivialScratchLet(right, Precedence::kAssignment);
4060 return this->assembleEqualityExpression(
left.type(), leftName,
right.type(), rightName,
4061 op, parentPrecedence);
4064void WGSLCodeGenerator::writeProgramElement(
const ProgramElement&
e) {
4072 case ProgramElement::Kind::kGlobalVar:
4073 this->writeGlobalVarDeclaration(
e.as<GlobalVarDeclaration>());
4075 case ProgramElement::Kind::kInterfaceBlock:
4079 case ProgramElement::Kind::kStructDefinition:
4080 this->writeStructDefinition(
e.as<StructDefinition>());
4082 case ProgramElement::Kind::kFunctionPrototype:
4090 this->writeFunction(
e.as<FunctionDefinition>());
4092 case ProgramElement::Kind::kModifiers:
4093 this->writeModifiersDeclaration(
e.as<ModifiersDeclaration>());
4096 SkDEBUGFAILF(
"unsupported program element: %s\n",
e.description().c_str());
4101void WGSLCodeGenerator::writeTextureOrSampler(
const Variable& var,
4102 int bindingLocation,
4104 std::string_view wgslType) {
4105 if (var.type().dimensions() !=
SpvDim2D) {
4111 this->write(
"@group(");
4113 this->write(
") @binding(");
4115 this->write(
") var ");
4116 this->write(this->assembleName(var.mangledName()));
4119 this->write(wgslType);
4120 this->writeLine(
";");
4123void WGSLCodeGenerator::writeGlobalVarDeclaration(
const GlobalVarDeclaration&
d) {
4124 const VarDeclaration& decl =
d.varDeclaration();
4125 const Variable& var = *decl.var();
4127 is_in_global_uniforms(var)) {
4137 int samplerLocation = var.layout().fSampler >= 0 ? var.layout().fSampler
4138 : 10000 + fScratchCount++;
4139 this->writeTextureOrSampler(var, samplerLocation, kSamplerSuffix,
"sampler");
4142 int textureLocation = var.layout().fTexture >= 0 ? var.layout().fTexture
4143 : 10000 + fScratchCount++;
4144 this->writeTextureOrSampler(var, textureLocation, kTextureSuffix,
"texture_2d<f32>");
4150 int textureLocation = var.layout().fBinding >= 0 ? var.layout().fBinding
4151 : 10000 + fScratchCount++;
4153 this->writeTextureOrSampler(var, textureLocation,
"",
4154 to_wgsl_type(
fContext, var.type(), &var.layout()));
4164 initializer += this->assembleExpression(*decl.value(), Precedence::kAssignment);
4167 if (var.modifierFlags().isConst()) {
4168 this->write(
"const ");
4169 }
else if (var.modifierFlags().isWorkgroup()) {
4170 this->write(
"var<workgroup> ");
4171 }
else if (var.modifierFlags().isPixelLocal()) {
4172 this->write(
"var<pixel_local> ");
4174 this->write(
"var<private> ");
4176 this->write(this->assembleName(var.mangledName()));
4177 this->write(
": " + to_wgsl_type(
fContext, var.type(), &var.layout()));
4179 this->writeLine(
";");
4182void WGSLCodeGenerator::writeStructDefinition(
const StructDefinition&
s) {
4184 this->writeLine(
"struct " +
type.displayName() +
" {");
4185 this->writeFields(
type.fields(),
nullptr);
4186 this->writeLine(
"};");
4189void WGSLCodeGenerator::writeModifiersDeclaration(
const ModifiersDeclaration& modifiers) {
4197 if (modifiers.layout().fLocalSizeX >= 0) {
4198 fLocalSizeX = modifiers.layout().fLocalSizeX;
4200 if (modifiers.layout().fLocalSizeY >= 0) {
4201 fLocalSizeY = modifiers.layout().fLocalSizeY;
4203 if (modifiers.layout().fLocalSizeZ >= 0) {
4204 fLocalSizeZ = modifiers.layout().fLocalSizeZ;
4208void WGSLCodeGenerator::writeFields(
SkSpan<const Field> fields,
const MemoryLayout* memoryLayout) {
4213 for (
size_t index = 0; index < fields.
size(); ++index) {
4214 const Field& field = fields[index];
4215 if (memoryLayout && !memoryLayout->isSupported(*field.fType)) {
4218 "' is not permitted here");
4224 if (index < fields.
size() - 1) {
4225 int thisFieldOffset = field.fLayout.fOffset;
4226 int nextFieldOffset = fields[index + 1].fLayout.fOffset;
4227 if (index == 0 && thisFieldOffset > 0) {
4231 if (thisFieldOffset >= 0 && nextFieldOffset > thisFieldOffset) {
4232 this->write(
"@size(");
4238 this->write(this->assembleName(field.fName));
4240 if (
const FieldPolyfillInfo*
info = fFieldPolyfillMap.find(&field)) {
4241 if (
info->fIsArray) {
4244 this->write(
"array<_skArrayElement_");
4245 this->write(field.fType->abbreviatedName());
4249 }
else if (
info->fIsMatrix) {
4250 this->write(
"_skMatrix");
4257 this->write(to_wgsl_type(
fContext, *field.fType, &field.fLayout));
4259 this->writeLine(
",");
4265void WGSLCodeGenerator::writeEnables() {
4266 this->writeLine(
"diagnostic(off, derivative_uniformity);");
4267 this->writeLine(
"diagnostic(off, chromium.unreachable_code);");
4270 this->writeLine(
"enable chromium_experimental_pixel_local;");
4273 this->writeLine(
"enable chromium_experimental_framebuffer_fetch;");
4276 this->writeLine(
"enable dual_source_blending;");
4280bool WGSLCodeGenerator::needsStageInputStruct()
const {
4283 return !fPipelineInputs.empty();
4286void WGSLCodeGenerator::writeStageInputStruct() {
4287 if (!this->needsStageInputStruct()) {
4291 std::string_view structNamePrefix = pipeline_struct_prefix(
fProgram.
fConfig->fKind);
4292 SkASSERT(!structNamePrefix.empty());
4294 this->write(
"struct ");
4295 this->write(structNamePrefix);
4296 this->writeLine(
"In {");
4299 for (
const Variable* v : fPipelineInputs) {
4300 if (v->type().isInterfaceBlock()) {
4301 for (
const Field&
f : v->type().fields()) {
4305 this->writePipelineIODeclaration(v->layout(), v->type(), v->mangledName(),
4311 this->writeLine(
"};");
4314bool WGSLCodeGenerator::needsStageOutputStruct()
const {
4321void WGSLCodeGenerator::writeStageOutputStruct() {
4322 if (!this->needsStageOutputStruct()) {
4326 std::string_view structNamePrefix = pipeline_struct_prefix(
fProgram.
fConfig->fKind);
4327 SkASSERT(!structNamePrefix.empty());
4329 this->write(
"struct ");
4330 this->write(structNamePrefix);
4331 this->writeLine(
"Out {");
4334 bool declaredPositionBuiltin =
false;
4335 bool requiresPointSizeBuiltin =
false;
4336 for (
const Variable* v : fPipelineOutputs) {
4337 if (v->type().isInterfaceBlock()) {
4338 for (
const auto&
f : v->type().fields()) {
4341 declaredPositionBuiltin =
true;
4346 requiresPointSizeBuiltin =
true;
4350 this->writePipelineIODeclaration(v->layout(), v->type(), v->mangledName(),
4357 if (positionBuiltinRequired && !declaredPositionBuiltin) {
4358 this->writeLine(
"@builtin(position) sk_Position: vec4<f32>,");
4362 this->writeLine(
"};");
4371 this->writeLine(
"/* unsupported */ var<private> sk_PointSize: f32;");
4375void WGSLCodeGenerator::prepareUniformPolyfillsForInterfaceBlock(
4376 const InterfaceBlock* interfaceBlock,
4377 std::string_view instanceName,
4382 const Type& structType = interfaceBlock->var()->type().componentType();
4383 for (
const Field& field : structType.fields()) {
4385 bool needsArrayPolyfill =
false;
4386 bool needsMatrixPolyfill =
false;
4388 auto isPolyfillableMatrixType = [&](
const Type*
type) {
4389 return type->isMatrix() && std140.stride(*
type) != native.stride(*
type);
4392 if (isPolyfillableMatrixType(
type)) {
4395 needsMatrixPolyfill =
true;
4396 }
else if (
type->isArray() && !
type->isUnsizedArray() &&
4397 !
type->componentType().isOpaque()) {
4398 const Type* innerType = &
type->componentType();
4399 if (isPolyfillableMatrixType(innerType)) {
4401 needsArrayPolyfill =
true;
4402 needsMatrixPolyfill =
true;
4403 }
else if (native.size(*innerType) < 16) {
4406 needsArrayPolyfill =
true;
4410 if (needsArrayPolyfill || needsMatrixPolyfill) {
4412 FieldPolyfillInfo
info;
4413 info.fInterfaceBlock = interfaceBlock;
4414 info.fReplacementName =
"_skUnpacked_" + std::string(instanceName) +
'_' +
4415 this->assembleName(field.fName);
4416 info.fIsArray = needsArrayPolyfill;
4417 info.fIsMatrix = needsMatrixPolyfill;
4418 fFieldPolyfillMap.set(&field,
info);
4423void WGSLCodeGenerator::writeUniformsAndBuffers() {
4426 if (!
e->is<InterfaceBlock>()) {
4429 const InterfaceBlock& ib =
e->as<InterfaceBlock>();
4432 std::string_view addressSpace;
4433 std::string_view accessMode;
4435 if (ib.var()->modifierFlags().isUniform()) {
4436 addressSpace =
"uniform";
4438 }
else if (ib.var()->modifierFlags().isBuffer()) {
4439 addressSpace =
"storage";
4441 accessMode = ib.var()->modifierFlags().isReadOnly() ?
", read" :
", read_write";
4447 std::string instanceName;
4448 if (ib.instanceName().empty()) {
4449 instanceName =
"_" + std::string(addressSpace) +
std::to_string(fScratchCount++);
4450 fInterfaceBlockNameMap[&ib.var()->type().componentType()] = instanceName;
4452 instanceName = std::string(ib.instanceName());
4455 this->prepareUniformPolyfillsForInterfaceBlock(&ib, instanceName, nativeLayout);
4459 this->write(
"struct ");
4460 this->write(ib.typeName());
4461 this->writeLine(
" {");
4464 const Type& ibType = ib.var()->type().componentType();
4471 this->writeFields(ibFields, &layout);
4472 this->writeLine(
"};");
4473 this->write(
"@group(");
4475 this->write(
") @binding(");
4477 this->write(
") var<");
4478 this->write(addressSpace);
4479 this->write(accessMode);
4481 this->write(instanceName);
4483 this->write(to_wgsl_type(
fContext, ib.var()->type(), &ib.var()->layout()));
4484 this->writeLine(
";");
4488void WGSLCodeGenerator::writeNonBlockUniformsForTests() {
4489 bool declaredUniformsStruct =
false;
4492 if (
e->is<GlobalVarDeclaration>()) {
4493 const GlobalVarDeclaration& decls =
e->as<GlobalVarDeclaration>();
4494 const Variable& var = *decls.varDeclaration().var();
4495 if (is_in_global_uniforms(var)) {
4496 if (!declaredUniformsStruct) {
4497 this->write(
"struct _GlobalUniforms {\n");
4498 declaredUniformsStruct =
true;
4501 this->writeVariableDecl(var.layout(), var.type(), var.mangledName(),
4506 if (declaredUniformsStruct) {
4509 this->write(
"};\n");
4512 this->writeLine(
"var<uniform> _globalUniforms: _GlobalUniforms;");
4516std::string WGSLCodeGenerator::functionDependencyArgs(
const FunctionDeclaration&
f) {
4519 if (deps && *deps) {
4520 const char* separator =
"";
4521 if (*deps & WGSLFunctionDependency::kPipelineInputs) {
4525 if (*deps & WGSLFunctionDependency::kPipelineOutputs) {
4527 args +=
"_stageOut";
4533bool WGSLCodeGenerator::writeFunctionDependencyParams(
const FunctionDeclaration&
f) {
4535 if (!deps || !*deps) {
4539 std::string_view structNamePrefix = pipeline_struct_prefix(
fProgram.
fConfig->fKind);
4540 if (structNamePrefix.empty()) {
4543 const char* separator =
"";
4544 if (*deps & WGSLFunctionDependency::kPipelineInputs) {
4545 this->write(
"_stageIn: ");
4547 this->write(structNamePrefix);
4550 if (*deps & WGSLFunctionDependency::kPipelineOutputs) {
4551 this->write(separator);
4552 this->write(
"_stageOut: ptr<function, ");
4553 this->write(structNamePrefix);
4554 this->write(
"Out>");
4559#if defined(SK_ENABLE_WGSL_VALIDATION)
4560static bool validate_wgsl(ErrorReporter&
reporter,
const std::string& wgsl, std::string* warnings) {
4562 tint::wgsl::reader::Options
options;
4563 for (
auto extension : {tint::wgsl::Extension::kChromiumExperimentalPixelLocal,
4564 tint::wgsl::Extension::kDualSourceBlending}) {
4565 options.allowed_features.extensions.insert(extension);
4569 tint::Source::File srcFile(
"", wgsl);
4572 if (program.Diagnostics().ContainsErrors()) {
4574#if defined(SKSL_STANDALONE)
4575 reporter.error(Position(), std::string(
"Tint compilation failed.\n\n") + wgsl);
4579 tint::diag::Formatter diagFormatter;
4580 std::string diagOutput = diagFormatter.Format(program.Diagnostics()).Plain();
4588 if (!program.Diagnostics().empty()) {
4590 tint::diag::Formatter diagFormatter;
4591 *warnings = diagFormatter.Format(program.Diagnostics()).Plain();
4602#ifdef SK_ENABLE_WGSL_VALIDATION
4607 std::string wgslString = wgsl.
str();
4608 std::string warnings;
4609 result = validate_wgsl(*program.
fContext->fErrors, wgslString, &warnings);
4610 if (!warnings.empty()) {
4611 out.writeText(
"/* Tint reported warnings. */\n\n");
4613 out.writeString(wgslString);
4619 program.
fContext->fErrors->setSource(std::string_view());
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
static struct Initializer initializer
static float prev(float f)
#define SkDEBUGFAIL(message)
#define SkDEBUGFAILF(fmt,...)
#define SkASSERTF(cond, fmt,...)
#define SK_MAKE_BITMASK_OPS(E)
static std::vector< SkPDFIndirectReference > sort(const THashSet< SkPDFIndirectReference > &src)
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
CodeGenerator(const Context *context, const ShaderCaps *caps, const Program *program, OutputStream *stream)
static const Expression * GetConstantValueOrNull(const Expression &value)
const BuiltinTypes & fTypes
void error(Position position, std::string_view msg)
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 bool IsIdentity(const ComponentArray &components)
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 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 float max(float r, float g, float b)
void SK_API Parse(Metadata &metadata, const SkData *data)
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
DEF_SWITCHES_START aot vmservice shared library name
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
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 defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
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 SkString to_string(int n)
static std::unique_ptr< Expression > LoadFloatBuffer(const Context &context, const SkSL::ShaderCaps &shaderCaps, Position position, std::unique_ptr< Expression > idx)
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)