Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Public Member Functions | Protected Types | Protected Member Functions | Protected Attributes | Static Protected Attributes | List of all members
SkSL::MetalCodeGenerator Class Reference
Inheritance diagram for SkSL::MetalCodeGenerator:
SkSL::CodeGenerator

Classes

class  GlobalStructVisitor
 
struct  IndexSubstitutionData
 
class  ThreadgroupStructVisitor
 

Public Member Functions

 MetalCodeGenerator (const Context *context, const ShaderCaps *caps, const Program *program, OutputStream *out)
 
bool generateCode () override
 
- Public Member Functions inherited from SkSL::CodeGenerator
 CodeGenerator (const Context *context, const ShaderCaps *caps, const Program *program, OutputStream *stream)
 
virtual ~CodeGenerator ()=default
 
OutputStreamoutputStream ()
 
void setOutputStream (OutputStream *output)
 

Protected Types

using Precedence = OperatorPrecedence
 
using Requirements = int
 
using IndexSubstitutionMap = skia_private::THashMap< const Expression *, std::string >
 
using INHERITED = CodeGenerator
 

Protected Member Functions

void visitGlobalStruct (GlobalStructVisitor *visitor)
 
void visitThreadgroupStruct (ThreadgroupStructVisitor *visitor)
 
void write (std::string_view s)
 
void writeLine (std::string_view s=std::string_view())
 
void finishLine ()
 
void writeHeader ()
 
void writeSampler2DPolyfill ()
 
void writeUniformStruct ()
 
void writeInputStruct ()
 
void writeOutputStruct ()
 
void writeInterfaceBlocks ()
 
void writeStructDefinitions ()
 
void writeConstantVariables ()
 
void writeFields (SkSpan< const Field > fields, Position pos)
 
int size (const Type *type, bool isPacked) const
 
int alignment (const Type *type, bool isPacked) const
 
void writeGlobalStruct ()
 
void writeGlobalInit ()
 
void writeThreadgroupStruct ()
 
void writeThreadgroupInit ()
 
void writePrecisionModifier ()
 
std::string typeName (const Type &type)
 
void writeStructDefinition (const StructDefinition &s)
 
void writeType (const Type &type)
 
void writeExtension (const Extension &ext)
 
void writeInterfaceBlock (const InterfaceBlock &intf)
 
void writeFunctionRequirementParams (const FunctionDeclaration &f, const char *&separator)
 
void writeFunctionRequirementArgs (const FunctionDeclaration &f, const char *&separator)
 
bool writeFunctionDeclaration (const FunctionDeclaration &f)
 
void writeFunction (const FunctionDefinition &f)
 
void writeFunctionPrototype (const FunctionPrototype &f)
 
void writeLayout (const Layout &layout)
 
void writeModifiers (ModifierFlags flags)
 
void writeVarInitializer (const Variable &var, const Expression &value)
 
void writeName (std::string_view name)
 
void writeVarDeclaration (const VarDeclaration &decl)
 
void writeFragCoord ()
 
void writeVariableReference (const VariableReference &ref)
 
void writeExpression (const Expression &expr, Precedence parentPrecedence)
 
void writeMinAbsHack (Expression &absExpr, Expression &otherExpr)
 
std::string getInversePolyfill (const ExpressionArray &arguments)
 
std::string getBitcastIntrinsic (const Type &outType)
 
std::string getTempVariable (const Type &varType)
 
void writeFunctionCall (const FunctionCall &c)
 
bool matrixConstructHelperIsNeeded (const ConstructorCompound &c)
 
std::string getMatrixConstructHelper (const AnyConstructor &c)
 
void assembleMatrixFromMatrix (const Type &sourceMatrix, int columns, int rows)
 
void assembleMatrixFromExpressions (const AnyConstructor &ctor, int columns, int rows)
 
void writeMatrixCompMult ()
 
void writeOuterProduct ()
 
void writeMatrixTimesEqualHelper (const Type &left, const Type &right, const Type &result)
 
void writeMatrixDivisionHelpers (const Type &type)
 
void writeMatrixEqualityHelpers (const Type &left, const Type &right)
 
std::string getVectorFromMat2x2ConstructorHelper (const Type &matrixType)
 
void writeArrayEqualityHelpers (const Type &type)
 
void writeStructEqualityHelpers (const Type &type)
 
void writeEqualityHelpers (const Type &leftType, const Type &rightType)
 
void writeArgumentList (const ExpressionArray &arguments)
 
void writeSimpleIntrinsic (const FunctionCall &c)
 
bool writeIntrinsicCall (const FunctionCall &c, IntrinsicKind kind)
 
void writeConstructorCompound (const ConstructorCompound &c, Precedence parentPrecedence)
 
void writeConstructorCompoundVector (const ConstructorCompound &c, Precedence parentPrecedence)
 
void writeConstructorCompoundMatrix (const ConstructorCompound &c, Precedence parentPrecedence)
 
void writeConstructorMatrixResize (const ConstructorMatrixResize &c, Precedence parentPrecedence)
 
void writeAnyConstructor (const AnyConstructor &c, const char *leftBracket, const char *rightBracket, Precedence parentPrecedence)
 
void writeCastConstructor (const AnyConstructor &c, const char *leftBracket, const char *rightBracket, Precedence parentPrecedence)
 
void writeConstructorArrayCast (const ConstructorArrayCast &c, Precedence parentPrecedence)
 
void writeFieldAccess (const FieldAccess &f)
 
void writeSwizzle (const Swizzle &swizzle)
 
void writeNumberAsMatrix (const Expression &expr, const Type &matrixType)
 
void writeBinaryExpressionElement (const Expression &expr, Operator op, const Expression &other, Precedence precedence)
 
void writeBinaryExpression (const BinaryExpression &b, Precedence parentPrecedence)
 
void writeTernaryExpression (const TernaryExpression &t, Precedence parentPrecedence)
 
void writeIndexExpression (const IndexExpression &expr)
 
void writeIndexInnerExpression (const Expression &expr)
 
void writePrefixExpression (const PrefixExpression &p, Precedence parentPrecedence)
 
void writePostfixExpression (const PostfixExpression &p, Precedence parentPrecedence)
 
void writeLiteral (const Literal &f)
 
void writeStatement (const Statement &s)
 
void writeStatements (const StatementArray &statements)
 
void writeBlock (const Block &b)
 
void writeIfStatement (const IfStatement &stmt)
 
void writeForStatement (const ForStatement &f)
 
void writeDoStatement (const DoStatement &d)
 
void writeExpressionStatement (const ExpressionStatement &s)
 
void writeSwitchStatement (const SwitchStatement &s)
 
void writeReturnStatementFromMain ()
 
void writeReturnStatement (const ReturnStatement &r)
 
void writeProgramElement (const ProgramElement &e)
 
Requirements requirements (const FunctionDeclaration &f)
 
Requirements requirements (const Statement *s)
 
void writeComputeMainInputs ()
 
int getUniformBinding (const Layout &layout)
 
int getUniformSet (const Layout &layout)
 
void writeWithIndexSubstitution (const std::function< void()> &fn)
 

Protected Attributes

skia_private::THashSet< std::string_view > fReservedWords
 
skia_private::THashMap< const Type *, std::string > fInterfaceBlockNameMap
 
int fAnonInterfaceCount = 0
 
int fPaddingCount = 0
 
const char * fLineEnding
 
std::string fFunctionHeader
 
StringStream fExtraFunctions
 
StringStream fExtraFunctionPrototypes
 
int fVarCount = 0
 
int fIndentation = 0
 
bool fAtLineStart = false
 
bool fFoundDerivatives = false
 
skia_private::THashMap< const FunctionDeclaration *, RequirementsfRequirements
 
skia_private::THashSet< std::string > fHelpers
 
int fUniformBuffer = -1
 
std::string fRTFlipName
 
const FunctionDeclarationfCurrentFunction = nullptr
 
int fSwizzleHelperCount = 0
 
std::unique_ptr< IndexSubstitutionDatafIndexSubstitutionData
 
bool fWrittenInverse2 = false
 
bool fWrittenInverse3 = false
 
bool fWrittenInverse4 = false
 
bool fWrittenMatrixCompMult = false
 
bool fWrittenOuterProduct = false
 
- Protected Attributes inherited from SkSL::CodeGenerator
const ProgramfProgram
 
Context fContext
 
const ShaderCapsfCaps
 
OutputStreamfOut
 

Static Protected Attributes

static constexpr Requirements kNo_Requirements = 0
 
static constexpr Requirements kInputs_Requirement = 1 << 0
 
static constexpr Requirements kOutputs_Requirement = 1 << 1
 
static constexpr Requirements kUniforms_Requirement = 1 << 2
 
static constexpr Requirements kGlobals_Requirement = 1 << 3
 
static constexpr Requirements kFragCoord_Requirement = 1 << 4
 
static constexpr Requirements kSampleMaskIn_Requirement = 1 << 5
 
static constexpr Requirements kVertexID_Requirement = 1 << 6
 
static constexpr Requirements kInstanceID_Requirement = 1 << 7
 
static constexpr Requirements kThreadgroups_Requirement = 1 << 8
 
static constexpr char kTextureSuffix [] = "_Tex"
 
static constexpr char kSamplerSuffix [] = "_Smplr"
 
- Static Protected Attributes inherited from SkSL::CodeGenerator
static constexpr float kSharpenTexturesBias = -.475f
 

Detailed Description

Definition at line 93 of file SkSLMetalCodeGenerator.cpp.

Member Typedef Documentation

◆ IndexSubstitutionMap

Definition at line 349 of file SkSLMetalCodeGenerator.cpp.

◆ INHERITED

Definition at line 365 of file SkSLMetalCodeGenerator.cpp.

◆ Precedence

Definition at line 106 of file SkSLMetalCodeGenerator.cpp.

◆ Requirements

Definition at line 108 of file SkSLMetalCodeGenerator.cpp.

Constructor & Destructor Documentation

◆ MetalCodeGenerator()

SkSL::MetalCodeGenerator::MetalCodeGenerator ( const Context context,
const ShaderCaps caps,
const Program program,
OutputStream out 
)
inline

Definition at line 95 of file SkSLMetalCodeGenerator.cpp.

99 : INHERITED(context, caps, program, out)
100 , fReservedWords({"atan2", "rsqrt", "rint", "dfdx", "dfdy", "vertex", "fragment"})
101 , fLineEnding("\n") {}
skia_private::THashSet< std::string_view > fReservedWords

Member Function Documentation

◆ alignment()

int SkSL::MetalCodeGenerator::alignment ( const Type type,
bool  isPacked 
) const
protected

◆ assembleMatrixFromExpressions()

void SkSL::MetalCodeGenerator::assembleMatrixFromExpressions ( const AnyConstructor ctor,
int  columns,
int  rows 
)
protected

Definition at line 1357 of file SkSLMetalCodeGenerator.cpp.

1359 {
1360 SkASSERT(rows <= 4);
1361 SkASSERT(columns <= 4);
1362
1363 std::string matrixType = this->typeName(ctor.type().componentType());
1364 size_t argIndex = 0;
1365 int argPosition = 0;
1366 auto args = ctor.argumentSpan();
1367
1368 static constexpr char kSwizzle[] = "xyzw";
1369 const char* separator = "";
1370 for (int c = 0; c < columns; ++c) {
1371 fExtraFunctions.printf("%s%s%d(", separator, matrixType.c_str(), rows);
1372 separator = "), ";
1373
1374 const char* columnSeparator = "";
1375 for (int r = 0; r < rows;) {
1376 fExtraFunctions.writeText(columnSeparator);
1377 columnSeparator = ", ";
1378
1379 if (argIndex < args.size()) {
1380 const Type& argType = args[argIndex]->type();
1381 switch (argType.typeKind()) {
1383 fExtraFunctions.printf("x%zu", argIndex);
1384 ++r;
1385 ++argPosition;
1386 break;
1387 }
1389 fExtraFunctions.printf("x%zu.", argIndex);
1390 do {
1391 fExtraFunctions.write8(kSwizzle[argPosition]);
1392 ++r;
1393 ++argPosition;
1394 } while (r < rows && argPosition < argType.columns());
1395 break;
1396 }
1398 fExtraFunctions.printf("x%zu[%d].", argIndex, argPosition / argType.rows());
1399 do {
1400 fExtraFunctions.write8(kSwizzle[argPosition]);
1401 ++r;
1402 ++argPosition;
1403 } while (r < rows && (argPosition % argType.rows()) != 0);
1404 break;
1405 }
1406 default: {
1407 SkDEBUGFAIL("incorrect type of argument for matrix constructor");
1408 fExtraFunctions.writeText("<error>");
1409 break;
1410 }
1411 }
1412
1413 if (argPosition >= argType.columns() * argType.rows()) {
1414 ++argIndex;
1415 argPosition = 0;
1416 }
1417 } else {
1418 SkDEBUGFAIL("not enough arguments for matrix constructor");
1419 fExtraFunctions.writeText("<error>");
1420 }
1421 }
1422 }
1423
1424 if (argPosition != 0 || argIndex != args.size()) {
1425 SkDEBUGFAIL("incorrect number of arguments for matrix constructor");
1426 fExtraFunctions.writeText(", <error>");
1427 }
1428
1430}
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SkASSERT(cond)
Definition SkAssert.h:116
std::string typeName(const Type &type)
void printf(const char format[],...) SK_PRINTF_LIKE(2
void write8(uint8_t b) override
void writeText(const char *s) override
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args

◆ assembleMatrixFromMatrix()

void SkSL::MetalCodeGenerator::assembleMatrixFromMatrix ( const Type sourceMatrix,
int  columns,
int  rows 
)
protected

Definition at line 1317 of file SkSLMetalCodeGenerator.cpp.

1317 {
1318 SkASSERT(rows <= 4);
1319 SkASSERT(columns <= 4);
1320
1321 std::string matrixType = this->typeName(sourceMatrix.componentType());
1322
1323 const char* separator = "";
1324 for (int c = 0; c < columns; ++c) {
1325 fExtraFunctions.printf("%s%s%d(", separator, matrixType.c_str(), rows);
1326 separator = "), ";
1327
1328 // Determine how many values to take from the source matrix for this row.
1329 int swizzleLength = 0;
1330 if (c < sourceMatrix.columns()) {
1331 swizzleLength = std::min<>(rows, sourceMatrix.rows());
1332 }
1333
1334 // Emit all the values from the source matrix row.
1335 bool firstItem;
1336 switch (swizzleLength) {
1337 case 0: firstItem = true; break;
1338 case 1: firstItem = false; fExtraFunctions.printf("x0[%d].x", c); break;
1339 case 2: firstItem = false; fExtraFunctions.printf("x0[%d].xy", c); break;
1340 case 3: firstItem = false; fExtraFunctions.printf("x0[%d].xyz", c); break;
1341 case 4: firstItem = false; fExtraFunctions.printf("x0[%d].xyzw", c); break;
1342 default: SkUNREACHABLE;
1343 }
1344
1345 // Emit the placeholder identity-matrix cells.
1346 for (int r = swizzleLength; r < rows; ++r) {
1347 fExtraFunctions.printf("%s%s", firstItem ? "" : ", ", (r == c) ? "1.0" : "0.0");
1348 firstItem = false;
1349 }
1350 }
1351
1353}
#define SkUNREACHABLE
Definition SkAssert.h:135

◆ finishLine()

void SkSL::MetalCodeGenerator::finishLine ( )
protected

Definition at line 412 of file SkSLMetalCodeGenerator.cpp.

412 {
413 if (!fAtLineStart) {
414 this->writeLine();
415 }
416}
void writeLine(std::string_view s=std::string_view())

◆ generateCode()

bool SkSL::MetalCodeGenerator::generateCode ( )
overridevirtual

Implements SkSL::CodeGenerator.

Definition at line 3540 of file SkSLMetalCodeGenerator.cpp.

3540 {
3541 StringStream header;
3542 {
3543 AutoOutputStream outputToHeader(this, &header, &fIndentation);
3544 this->writeHeader();
3545 this->writeConstantVariables();
3546 this->writeSampler2DPolyfill();
3547 this->writeStructDefinitions();
3548 this->writeUniformStruct();
3549 this->writeInputStruct();
3551 this->writeOutputStruct();
3552 }
3553 this->writeInterfaceBlocks();
3554 this->writeGlobalStruct();
3555 this->writeThreadgroupStruct();
3556
3557 // Emit prototypes for every built-in function; these aren't always added in perfect order.
3558 for (const ProgramElement* e : fProgram.fSharedElements) {
3559 if (e->is<FunctionDefinition>()) {
3560 this->writeFunctionDeclaration(e->as<FunctionDefinition>().declaration());
3561 this->writeLine(";");
3562 }
3563 }
3564 }
3565 StringStream body;
3566 {
3567 AutoOutputStream outputToBody(this, &body, &fIndentation);
3568
3569 for (const ProgramElement* e : fProgram.elements()) {
3570 this->writeProgramElement(*e);
3571 }
3572 }
3576 write_stringstream(body, *fOut);
3577 return fContext.fErrors->errorCount() == 0;
3578}
const Program & fProgram
ErrorReporter * fErrors
Definition SkSLContext.h:36
bool writeFunctionDeclaration(const FunctionDeclaration &f)
void writeProgramElement(const ProgramElement &e)
void write_stringstream(const StringStream &s, OutputStream &out)
Definition SkSLUtil.cpp:41
static const char header[]
Definition skpbench.cpp:88
static bool IsCompute(ProgramKind kind)
std::unique_ptr< ProgramConfig > fConfig

◆ getBitcastIntrinsic()

std::string SkSL::MetalCodeGenerator::getBitcastIntrinsic ( const Type outType)
protected

Definition at line 564 of file SkSLMetalCodeGenerator.cpp.

564 {
565 return "as_type<" + outType.displayName() + ">";
566}

◆ getInversePolyfill()

std::string SkSL::MetalCodeGenerator::getInversePolyfill ( const ExpressionArray arguments)
protected

Definition at line 793 of file SkSLMetalCodeGenerator.cpp.

793 {
794 // Only use polyfills for a function taking a single-argument square matrix.
795 SkASSERT(arguments.size() == 1);
796 const Type& type = arguments.front()->type();
797 if (type.isMatrix() && type.rows() == type.columns()) {
798 switch (type.rows()) {
799 case 2:
800 if (!fWrittenInverse2) {
801 fWrittenInverse2 = true;
803 }
804 return "mat2_inverse";
805 case 3:
806 if (!fWrittenInverse3) {
807 fWrittenInverse3 = true;
809 }
810 return "mat3_inverse";
811 case 4:
812 if (!fWrittenInverse4) {
813 fWrittenInverse4 = true;
815 }
816 return "mat4_inverse";
817 }
818 }
819 SkDEBUGFAILF("no polyfill for inverse(%s)", type.description().c_str());
820 return "inverse";
821}
#define SkDEBUGFAILF(fmt,...)
Definition SkAssert.h:119
static constexpr char kInverse3x3[]
static constexpr char kInverse2x2[]
static constexpr char kInverse4x4[]

◆ getMatrixConstructHelper()

std::string SkSL::MetalCodeGenerator::getMatrixConstructHelper ( const AnyConstructor c)
protected

Definition at line 1436 of file SkSLMetalCodeGenerator.cpp.

1436 {
1437 const Type& type = c.type();
1438 int columns = type.columns();
1439 int rows = type.rows();
1440 auto args = c.argumentSpan();
1441 std::string typeName = this->typeName(type);
1442
1443 // Create the helper-method name and use it as our lookup key.
1444 std::string name = String::printf("%s_from", typeName.c_str());
1445 for (const std::unique_ptr<Expression>& expr : args) {
1446 String::appendf(&name, "_%s", this->typeName(expr->type()).c_str());
1447 }
1448
1449 // If a helper-method has not been synthesized yet, create it now.
1450 if (!fHelpers.contains(name)) {
1451 fHelpers.add(name);
1452
1453 // Unlike GLSL, Metal requires that matrices are initialized with exactly R vectors of C
1454 // components apiece. (In Metal 2.0, you can also supply R*C scalars, but you still cannot
1455 // supply a mixture of scalars and vectors.)
1456 fExtraFunctions.printf("%s %s(", typeName.c_str(), name.c_str());
1457
1458 size_t argIndex = 0;
1459 const char* argSeparator = "";
1460 for (const std::unique_ptr<Expression>& expr : args) {
1461 fExtraFunctions.printf("%s%s x%zu", argSeparator,
1462 this->typeName(expr->type()).c_str(), argIndex++);
1463 argSeparator = ", ";
1464 }
1465
1466 fExtraFunctions.printf(") {\n return %s(", typeName.c_str());
1467
1468 if (args.size() == 1 && args.front()->type().isMatrix()) {
1469 this->assembleMatrixFromMatrix(args.front()->type(), columns, rows);
1470 } else {
1471 this->assembleMatrixFromExpressions(c, columns, rows);
1472 }
1473
1474 fExtraFunctions.writeText(");\n}\n");
1475 }
1476 return name;
1477}
void assembleMatrixFromExpressions(const AnyConstructor &ctor, int columns, int rows)
skia_private::THashSet< std::string > fHelpers
void assembleMatrixFromMatrix(const Type &sourceMatrix, int columns, int rows)
void add(T item)
Definition SkTHash.h:573
bool contains(const T &item) const
Definition SkTHash.h:576
const char * name
Definition fuchsia.cc:50
std::string printf(const char *fmt,...) SK_PRINTF_LIKE(1
std::string void appendf(std::string *str, const char *fmt,...) SK_PRINTF_LIKE(2

◆ getTempVariable()

std::string SkSL::MetalCodeGenerator::getTempVariable ( const Type varType)
protected

Definition at line 852 of file SkSLMetalCodeGenerator.cpp.

852 {
853 std::string tempVar = "_skTemp" + std::to_string(fVarCount++);
854 this->fFunctionHeader += " " + this->typeName(type) + " " + tempVar + ";\n";
855 return tempVar;
856}

◆ getUniformBinding()

int SkSL::MetalCodeGenerator::getUniformBinding ( const Layout layout)
protected

Definition at line 2390 of file SkSLMetalCodeGenerator.cpp.

2390 {
2391 return (layout.fBinding >= 0) ? layout.fBinding
2392 : fProgram.fConfig->fSettings.fDefaultUniformBinding;
2393}

◆ getUniformSet()

int SkSL::MetalCodeGenerator::getUniformSet ( const Layout layout)
protected

Definition at line 2395 of file SkSLMetalCodeGenerator.cpp.

2395 {
2396 return (layout.fSet >= 0) ? layout.fSet
2397 : fProgram.fConfig->fSettings.fDefaultUniformSet;
2398}

◆ getVectorFromMat2x2ConstructorHelper()

std::string SkSL::MetalCodeGenerator::getVectorFromMat2x2ConstructorHelper ( const Type matrixType)
protected

Definition at line 1575 of file SkSLMetalCodeGenerator.cpp.

1575 {
1576 SkASSERT(matrixType.isMatrix());
1577 SkASSERT(matrixType.rows() == 2);
1578 SkASSERT(matrixType.columns() == 2);
1579
1580 std::string baseType = this->typeName(matrixType.componentType());
1581 std::string name = String::printf("%s4_from_%s2x2", baseType.c_str(), baseType.c_str());
1582 if (!fHelpers.contains(name)) {
1583 fHelpers.add(name);
1584
1586%s4 %s(%s2x2 x) {
1587 return %s4(x[0].xy, x[1].xy);
1588}
1589)", baseType.c_str(), name.c_str(), baseType.c_str(), baseType.c_str());
1590 }
1591
1592 return name;
1593}

◆ matrixConstructHelperIsNeeded()

bool SkSL::MetalCodeGenerator::matrixConstructHelperIsNeeded ( const ConstructorCompound c)
protected

Definition at line 1479 of file SkSLMetalCodeGenerator.cpp.

1479 {
1480 SkASSERT(c.type().isMatrix());
1481
1482 // GLSL is fairly free-form about inputs to its matrix constructors, but Metal is not; it
1483 // expects exactly R vectors of C components apiece. (Metal 2.0 also allows a list of R*C
1484 // scalars.) Some cases are simple to translate and so we handle those inline--e.g. a list of
1485 // scalars can be constructed trivially. In more complex cases, we generate a helper function
1486 // that converts our inputs into a properly-shaped matrix.
1487 // A matrix construct helper method is always used if any input argument is a matrix.
1488 // Helper methods are also necessary when any argument would span multiple rows. For instance:
1489 //
1490 // float2 x = (1, 2);
1491 // float3x2(x, 3, 4, 5, 6) = | 1 3 5 | = no helper needed; conversion can be done inline
1492 // | 2 4 6 |
1493 //
1494 // float2 x = (2, 3);
1495 // float3x2(1, x, 4, 5, 6) = | 1 3 5 | = x spans multiple rows; a helper method will be used
1496 // | 2 4 6 |
1497 //
1498 // float4 x = (1, 2, 3, 4);
1499 // float2x2(x) = | 1 3 | = x spans multiple rows; a helper method will be used
1500 // | 2 4 |
1501 //
1502
1503 int position = 0;
1504 for (const std::unique_ptr<Expression>& expr : c.arguments()) {
1505 // If an input argument is a matrix, we need a helper function.
1506 if (expr->type().isMatrix()) {
1507 return true;
1508 }
1509 position += expr->type().columns();
1510 if (position > c.type().rows()) {
1511 // An input argument would span multiple rows; a helper function is required.
1512 return true;
1513 }
1514 if (position == c.type().rows()) {
1515 // We've advanced to the end of a row. Wrap to the start of the next row.
1516 position = 0;
1517 }
1518 }
1519
1520 return false;
1521}

◆ requirements() [1/2]

MetalCodeGenerator::Requirements SkSL::MetalCodeGenerator::requirements ( const FunctionDeclaration f)
protected

Definition at line 3519 of file SkSLMetalCodeGenerator.cpp.

3519 {
3520 Requirements* found = fRequirements.find(&f);
3521 if (!found) {
3523 for (const ProgramElement* e : fProgram.elements()) {
3524 if (e->is<FunctionDefinition>()) {
3525 const FunctionDefinition& def = e->as<FunctionDefinition>();
3526 if (&def.declaration() == &f) {
3527 Requirements reqs = this->requirements(def.body().get());
3528 fRequirements.set(&f, reqs);
3529 return reqs;
3530 }
3531 }
3532 }
3533 // We never found a definition for this declared function, but it's legal to prototype a
3534 // function without ever giving a definition, as long as you don't call it.
3535 return kNo_Requirements;
3536 }
3537 return *found;
3538}
skia_private::THashMap< const FunctionDeclaration *, Requirements > fRequirements
static constexpr Requirements kNo_Requirements
Requirements requirements(const FunctionDeclaration &f)

◆ requirements() [2/2]

MetalCodeGenerator::Requirements SkSL::MetalCodeGenerator::requirements ( const Statement s)
protected

Definition at line 3452 of file SkSLMetalCodeGenerator.cpp.

3452 {
3453 class RequirementsVisitor : public ProgramVisitor {
3454 public:
3455 using ProgramVisitor::visitStatement;
3456
3457 bool visitExpression(const Expression& e) override {
3458 switch (e.kind()) {
3459 case Expression::Kind::kFunctionCall: {
3460 const FunctionCall& f = e.as<FunctionCall>();
3461 fRequirements |= fCodeGen->requirements(f.function());
3462 break;
3463 }
3464 case Expression::Kind::kFieldAccess: {
3465 const FieldAccess& f = e.as<FieldAccess>();
3466 if (f.ownerKind() == FieldAccess::OwnerKind::kAnonymousInterfaceBlock) {
3467 fRequirements |= kGlobals_Requirement;
3468 return false; // don't recurse into the base variable
3469 }
3470 break;
3471 }
3472 case Expression::Kind::kVariableReference: {
3473 const Variable& var = *e.as<VariableReference>().variable();
3474
3475 if (var.layout().fBuiltin == SK_FRAGCOORD_BUILTIN) {
3476 fRequirements |= kGlobals_Requirement | kFragCoord_Requirement;
3477 } else if (var.layout().fBuiltin == SK_SAMPLEMASKIN_BUILTIN) {
3478 fRequirements |= kSampleMaskIn_Requirement;
3479 } else if (var.layout().fBuiltin == SK_SAMPLEMASK_BUILTIN) {
3480 fRequirements |= kOutputs_Requirement;
3481 } else if (var.layout().fBuiltin == SK_VERTEXID_BUILTIN) {
3482 fRequirements |= kVertexID_Requirement;
3483 } else if (var.layout().fBuiltin == SK_INSTANCEID_BUILTIN) {
3484 fRequirements |= kInstanceID_Requirement;
3485 } else if (var.storage() == Variable::Storage::kGlobal) {
3486 if (is_input(var)) {
3487 fRequirements |= kInputs_Requirement;
3488 } else if (is_output(var)) {
3489 fRequirements |= kOutputs_Requirement;
3490 } else if (is_uniforms(var)) {
3491 fRequirements |= kUniforms_Requirement;
3492 } else if (is_threadgroup(var)) {
3493 fRequirements |= kThreadgroups_Requirement;
3494 } else if (is_in_globals(var)) {
3495 fRequirements |= kGlobals_Requirement;
3496 }
3497 }
3498 break;
3499 }
3500 default:
3501 break;
3502 }
3503 return INHERITED::visitExpression(e);
3504 }
3505
3506 MetalCodeGenerator* fCodeGen;
3507 Requirements fRequirements = kNo_Requirements;
3508 using INHERITED = ProgramVisitor;
3509 };
3510
3511 RequirementsVisitor visitor;
3512 if (s) {
3513 visitor.fCodeGen = this;
3514 visitor.visitStatement(*s);
3515 }
3516 return visitor.fRequirements;
3517}
#define INHERITED(method,...)
constexpr int SK_SAMPLEMASK_BUILTIN
constexpr int SK_VERTEXID_BUILTIN
constexpr int SK_INSTANCEID_BUILTIN
constexpr int SK_FRAGCOORD_BUILTIN
constexpr int SK_SAMPLEMASKIN_BUILTIN
struct MyStruct s
static bool is_uniforms(const Variable &var)
static bool is_in_globals(const Variable &var)
static bool is_input(const Variable &var)
static bool is_threadgroup(const Variable &var)
static bool is_output(const Variable &var)

◆ size()

int SkSL::MetalCodeGenerator::size ( const Type type,
bool  isPacked 
) const
protected

◆ typeName()

std::string SkSL::MetalCodeGenerator::typeName ( const Type type)
protected

Definition at line 422 of file SkSLMetalCodeGenerator.cpp.

422 {
423 // we need to know the modifiers for textures
424 const Type& type = raw.resolve().scalarTypeForLiteral();
425 switch (type.typeKind()) {
427 SkASSERT(!type.isUnsizedArray());
428 SkASSERTF(type.columns() > 0, "invalid array size: %s", type.description().c_str());
429 return String::printf("array<%s, %d>",
430 this->typeName(type.componentType()).c_str(), type.columns());
431
433 return this->typeName(type.componentType()) + std::to_string(type.columns());
434
436 return this->typeName(type.componentType()) + std::to_string(type.columns()) + "x" +
437 std::to_string(type.rows());
438
440 if (type.dimensions() != SpvDim2D) {
441 fContext.fErrors->error(Position(), "Unsupported texture dimensions");
442 }
443 return "sampler2D";
444
446 switch (type.textureAccess()) {
447 case Type::TextureAccess::kSample: return "texture2d<half>";
448 case Type::TextureAccess::kRead: return "texture2d<half, access::read>";
449 case Type::TextureAccess::kWrite: return "texture2d<half, access::write>";
450 case Type::TextureAccess::kReadWrite: return "texture2d<half, access::read_write>";
451 default: break;
452 }
454
456 // SkSL currently only supports the atomicUint type.
458 return "atomic_uint";
459
460 default:
461 return std::string(type.name());
462 }
463}
#define SkASSERTF(cond, fmt,...)
Definition SkAssert.h:117
const std::unique_ptr< Type > fAtomicUInt
const BuiltinTypes & fTypes
Definition SkSLContext.h:30
void error(Position position, std::string_view msg)
@ SpvDim2D
Definition spirv.h:143

◆ visitGlobalStruct()

void SkSL::MetalCodeGenerator::visitGlobalStruct ( GlobalStructVisitor visitor)
protected

Definition at line 3188 of file SkSLMetalCodeGenerator.cpp.

3188 {
3189 for (const ProgramElement* element : fProgram.elements()) {
3190 if (element->is<InterfaceBlock>()) {
3191 const auto* ib = &element->as<InterfaceBlock>();
3192 if (ib->typeName() != "sk_PerVertex") {
3193 visitor->visitInterfaceBlock(*ib, fInterfaceBlockNameMap[&ib->var()->type()]);
3194 }
3195 continue;
3196 }
3197 if (!element->is<GlobalVarDeclaration>()) {
3198 continue;
3199 }
3200 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
3201 const VarDeclaration& decl = global.varDeclaration();
3202 const Variable& var = *decl.var();
3203 if (decl.baseType().typeKind() == Type::TypeKind::kSampler) {
3204 visitor->visitSampler(var.type(), var.mangledName());
3205 continue;
3206 }
3207 if (decl.baseType().typeKind() == Type::TypeKind::kTexture) {
3208 visitor->visitTexture(var.type(), var.mangledName());
3209 continue;
3210 }
3211 if (!(var.modifierFlags() & ~ModifierFlag::kConst) && var.layout().fBuiltin == -1) {
3212 if (is_in_globals(var)) {
3213 // Visit a regular global variable.
3214 visitor->visitNonconstantVariable(var, decl.value().get());
3215 } else {
3216 // Visit a constant-expression variable.
3217 SkASSERT(var.modifierFlags().isConst());
3218 visitor->visitConstantVariable(decl);
3219 }
3220 }
3221 }
3222}
skia_private::THashMap< const Type *, std::string > fInterfaceBlockNameMap

◆ visitThreadgroupStruct()

void SkSL::MetalCodeGenerator::visitThreadgroupStruct ( ThreadgroupStructVisitor visitor)
protected

Definition at line 3345 of file SkSLMetalCodeGenerator.cpp.

3345 {
3346 for (const ProgramElement* element : fProgram.elements()) {
3347 if (!element->is<GlobalVarDeclaration>()) {
3348 continue;
3349 }
3350 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
3351 const VarDeclaration& decl = global.varDeclaration();
3352 const Variable& var = *decl.var();
3353 if (var.modifierFlags().isWorkgroup()) {
3354 SkASSERT(!decl.value());
3355 SkASSERT(!var.modifierFlags().isConst());
3356 visitor->visitNonconstantVariable(var);
3357 }
3358 }
3359}

◆ write()

void SkSL::MetalCodeGenerator::write ( std::string_view  s)
protected

Definition at line 391 of file SkSLMetalCodeGenerator.cpp.

391 {
392 if (s.empty()) {
393 return;
394 }
395#if defined(SK_DEBUG) || defined(SKSL_STANDALONE)
396 if (fAtLineStart) {
397 for (int i = 0; i < fIndentation; i++) {
398 fOut->writeText(" ");
399 }
400 }
401#endif
402 fOut->writeText(std::string(s).c_str());
403 fAtLineStart = false;
404}
virtual void writeText(const char *s)=0

◆ writeAnyConstructor()

void SkSL::MetalCodeGenerator::writeAnyConstructor ( const AnyConstructor c,
const char *  leftBracket,
const char *  rightBracket,
Precedence  parentPrecedence 
)
protected

Definition at line 1665 of file SkSLMetalCodeGenerator.cpp.

1668 {
1669 this->writeType(c.type());
1670 this->write(leftBracket);
1671 const char* separator = "";
1672 for (const std::unique_ptr<Expression>& arg : c.argumentSpan()) {
1673 this->write(separator);
1674 separator = ", ";
1675 this->writeExpression(*arg, Precedence::kSequence);
1676 }
1677 this->write(rightBracket);
1678}
void writeExpression(const Expression &expr, Precedence parentPrecedence)
void write(std::string_view s)

◆ writeArgumentList()

void SkSL::MetalCodeGenerator::writeArgumentList ( const ExpressionArray arguments)
protected

Definition at line 864 of file SkSLMetalCodeGenerator.cpp.

864 {
865 this->write("(");
866 const char* separator = "";
867 for (const std::unique_ptr<Expression>& arg : arguments) {
868 this->write(separator);
869 separator = ", ";
870 this->writeExpression(*arg, Precedence::kSequence);
871 }
872 this->write(")");
873}

◆ writeArrayEqualityHelpers()

void SkSL::MetalCodeGenerator::writeArrayEqualityHelpers ( const Type type)
protected

Definition at line 1986 of file SkSLMetalCodeGenerator.cpp.

1986 {
1987 SkASSERT(type.isArray());
1988
1989 // If the array's component type needs a helper as well, we need to emit that one first.
1990 this->writeEqualityHelpers(type.componentType(), type.componentType());
1991
1992 std::string key = "ArrayEquality []";
1993 if (!fHelpers.contains(key)) {
1994 fHelpers.add(key);
1996template <typename T1, typename T2>
1997bool operator==(const array_ref<T1> left, const array_ref<T2> right);
1998template <typename T1, typename T2>
1999bool operator!=(const array_ref<T1> left, const array_ref<T2> right);
2000)");
2002template <typename T1, typename T2>
2003bool operator==(const array_ref<T1> left, const array_ref<T2> right) {
2004 if (left.size() != right.size()) {
2005 return false;
2006 }
2007 for (size_t index = 0; index < left.size(); ++index) {
2008 if (!all(left[index] == right[index])) {
2009 return false;
2010 }
2011 }
2012 return true;
2013}
2014
2015template <typename T1, typename T2>
2016bool operator!=(const array_ref<T1> left, const array_ref<T2> right) {
2017 return !(left == right);
2018}
2019)");
2020 }
2021}
void writeEqualityHelpers(const Type &leftType, const Type &rightType)

◆ writeBinaryExpression()

void SkSL::MetalCodeGenerator::writeBinaryExpression ( const BinaryExpression b,
Precedence  parentPrecedence 
)
protected

Definition at line 2132 of file SkSLMetalCodeGenerator.cpp.

2133 {
2134 const Expression& left = *b.left();
2135 const Expression& right = *b.right();
2136 const Type& leftType = left.type();
2137 const Type& rightType = right.type();
2138 Operator op = b.getOperator();
2139 Precedence precedence = op.getBinaryPrecedence();
2140 bool needParens = precedence >= parentPrecedence;
2141 switch (op.kind()) {
2142 case Operator::Kind::EQEQ:
2143 this->writeEqualityHelpers(leftType, rightType);
2144 if (leftType.isVector()) {
2145 this->write("all");
2146 needParens = true;
2147 }
2148 break;
2149 case Operator::Kind::NEQ:
2150 this->writeEqualityHelpers(leftType, rightType);
2151 if (leftType.isVector()) {
2152 this->write("any");
2153 needParens = true;
2154 }
2155 break;
2156 default:
2157 break;
2158 }
2159 if (leftType.isMatrix() && rightType.isMatrix() && op.kind() == Operator::Kind::STAREQ) {
2160 this->writeMatrixTimesEqualHelper(leftType, rightType, b.type());
2161 }
2162 if (op.removeAssignment().kind() == Operator::Kind::SLASH &&
2163 ((leftType.isMatrix() && rightType.isMatrix()) ||
2164 (leftType.isScalar() && rightType.isMatrix()) ||
2165 (leftType.isMatrix() && rightType.isScalar()))) {
2166 this->writeMatrixDivisionHelpers(leftType.isMatrix() ? leftType : rightType);
2167 }
2168
2169 if (needParens) {
2170 this->write("(");
2171 }
2172
2173 // Some expressions need to be rewritten from `lhs *= rhs` to `lhs = lhs * rhs`, e.g.:
2174 // float4 x = float4(1);
2175 // x.xy *= float2x2(...);
2176 // will report the error "non-const reference cannot bind to vector element."
2177 if (op.isCompoundAssignment() && left.kind() == Expression::Kind::kSwizzle) {
2178 // We need to do the rewrite. This could be dangerous if the lhs contains an index
2179 // expression with a side effect (such as `array[Func()]`), so we enable index-substitution
2180 // here for the LHS; any index-expression with side effects will be evaluated into a scratch
2181 // variable.
2182 this->writeWithIndexSubstitution([&] {
2183 this->writeExpression(left, precedence);
2184 this->write(" = ");
2185 this->writeExpression(left, Precedence::kAssignment);
2186 this->write(operator_name(op.removeAssignment()));
2187
2188 // We never want to create index-expression substitutes on the RHS of the expression;
2189 // the RHS is only emitted one time.
2190 fIndexSubstitutionData->fCreateSubstitutes = false;
2191
2192 this->writeBinaryExpressionElement(right, op, left,
2193 op.removeAssignment().getBinaryPrecedence());
2194 });
2195 } else {
2196 // We don't need any rewrite; emit the binary expression as-is.
2197 this->writeBinaryExpressionElement(left, op, right, precedence);
2198 this->write(operator_name(op));
2199 this->writeBinaryExpressionElement(right, op, left, precedence);
2200 }
2201
2202 if (needParens) {
2203 this->write(")");
2204 }
2205}
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
void writeMatrixDivisionHelpers(const Type &type)
void writeBinaryExpressionElement(const Expression &expr, Operator op, const Expression &other, Precedence precedence)
void writeWithIndexSubstitution(const std::function< void()> &fn)
std::unique_ptr< IndexSubstitutionData > fIndexSubstitutionData
void writeMatrixTimesEqualHelper(const Type &left, const Type &right, const Type &result)
static bool b
static const char * operator_name(Operator op)

◆ writeBinaryExpressionElement()

void SkSL::MetalCodeGenerator::writeBinaryExpressionElement ( const Expression expr,
Operator  op,
const Expression other,
Precedence  precedence 
)
protected

Definition at line 2114 of file SkSLMetalCodeGenerator.cpp.

2117 {
2118 bool needMatrixSplatOnScalar = other.type().isMatrix() && expr.type().isNumber() &&
2119 op.isValidForMatrixOrVector() &&
2120 op.removeAssignment().kind() != Operator::Kind::STAR;
2121 if (needMatrixSplatOnScalar) {
2122 this->writeNumberAsMatrix(expr, other.type());
2123 } else if (op.isEquality() && expr.type().isArray()) {
2124 this->write("make_array_ref(");
2125 this->writeExpression(expr, precedence);
2126 this->write(")");
2127 } else {
2128 this->writeExpression(expr, precedence);
2129 }
2130}
void writeNumberAsMatrix(const Expression &expr, const Type &matrixType)

◆ writeBlock()

void SkSL::MetalCodeGenerator::writeBlock ( const Block b)
protected

Definition at line 2837 of file SkSLMetalCodeGenerator.cpp.

2837 {
2838 // Write scope markers if this block is a scope, or if the block is empty (since we need to emit
2839 // something here to make the code valid).
2840 bool isScope = b.isScope() || b.isEmpty();
2841 if (isScope) {
2842 this->writeLine("{");
2843 fIndentation++;
2844 }
2845 for (const std::unique_ptr<Statement>& stmt : b.children()) {
2846 if (!stmt->isEmpty()) {
2847 this->writeStatement(*stmt);
2848 this->finishLine();
2849 }
2850 }
2851 if (isScope) {
2852 fIndentation--;
2853 this->write("}");
2854 }
2855}
void writeStatement(const Statement &s)

◆ writeCastConstructor()

void SkSL::MetalCodeGenerator::writeCastConstructor ( const AnyConstructor c,
const char *  leftBracket,
const char *  rightBracket,
Precedence  parentPrecedence 
)
protected

Definition at line 1680 of file SkSLMetalCodeGenerator.cpp.

1683 {
1684 return this->writeAnyConstructor(c, leftBracket, rightBracket, parentPrecedence);
1685}
void writeAnyConstructor(const AnyConstructor &c, const char *leftBracket, const char *rightBracket, Precedence parentPrecedence)

◆ writeComputeMainInputs()

void SkSL::MetalCodeGenerator::writeComputeMainInputs ( )
protected

Definition at line 2593 of file SkSLMetalCodeGenerator.cpp.

2593 {
2594 // Compute shaders only have input variables (e.g. sk_GlobalInvocationID) and access program
2595 // inputs/outputs via the Globals and Uniforms structs. We collect the allowed "in" parameters
2596 // into an Input struct here, since the rest of the code expects the normal _in / _out pattern.
2597 this->write("Inputs _in = { ");
2598 const char* separator = "";
2599 for (const ProgramElement* e : fProgram.elements()) {
2600 if (e->is<GlobalVarDeclaration>()) {
2601 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
2602 const Variable* var = decls.varDeclaration().var();
2603 if (is_input(*var)) {
2604 this->write(separator);
2605 separator = ", ";
2606 this->writeName(var->mangledName());
2607 }
2608 }
2609 }
2610 this->writeLine(" };");
2611}
void writeName(std::string_view name)

◆ writeConstantVariables()

void SkSL::MetalCodeGenerator::writeConstantVariables ( )
protected

Definition at line 3172 of file SkSLMetalCodeGenerator.cpp.

3172 {
3173 class : public GlobalStructVisitor {
3174 public:
3175 void visitConstantVariable(const VarDeclaration& decl) override {
3176 fCodeGen->write("constant ");
3177 fCodeGen->writeVarDeclaration(decl);
3178 fCodeGen->finishLine();
3179 }
3180
3181 MetalCodeGenerator* fCodeGen = nullptr;
3182 } visitor;
3183
3184 visitor.fCodeGen = this;
3185 this->visitGlobalStruct(&visitor);
3186}
void visitGlobalStruct(GlobalStructVisitor *visitor)
MetalCodeGenerator(const Context *context, const ShaderCaps *caps, const Program *program, OutputStream *out)

◆ writeConstructorArrayCast()

void SkSL::MetalCodeGenerator::writeConstructorArrayCast ( const ConstructorArrayCast c,
Precedence  parentPrecedence 
)
protected

Definition at line 1544 of file SkSLMetalCodeGenerator.cpp.

1545 {
1546 const Type& inType = c.argument()->type().componentType();
1547 const Type& outType = c.type().componentType();
1548 std::string inTypeName = this->typeName(inType);
1549 std::string outTypeName = this->typeName(outType);
1550
1551 std::string name = "array_of_" + outTypeName + "_from_" + inTypeName;
1552 if (!fHelpers.contains(name)) {
1553 fHelpers.add(name);
1555template <size_t N>
1556array<%s, N> %s(thread const array<%s, N>& x) {
1557 array<%s, N> result;
1558 for (int i = 0; i < N; ++i) {
1559 result[i] = %s(x[i]);
1560 }
1561 return result;
1562}
1563)",
1564 outTypeName.c_str(), name.c_str(), inTypeName.c_str(),
1565 outTypeName.c_str(),
1566 outTypeName.c_str());
1567 }
1568
1569 this->write(name);
1570 this->write("(");
1571 this->writeExpression(*c.argument(), Precedence::kSequence);
1572 this->write(")");
1573}

◆ writeConstructorCompound()

void SkSL::MetalCodeGenerator::writeConstructorCompound ( const ConstructorCompound c,
Precedence  parentPrecedence 
)
protected

Definition at line 1533 of file SkSLMetalCodeGenerator.cpp.

1534 {
1535 if (c.type().isVector()) {
1536 this->writeConstructorCompoundVector(c, parentPrecedence);
1537 } else if (c.type().isMatrix()) {
1538 this->writeConstructorCompoundMatrix(c, parentPrecedence);
1539 } else {
1540 fContext.fErrors->error(c.fPosition, "unsupported compound constructor");
1541 }
1542}
void writeConstructorCompoundVector(const ConstructorCompound &c, Precedence parentPrecedence)
void writeConstructorCompoundMatrix(const ConstructorCompound &c, Precedence parentPrecedence)

◆ writeConstructorCompoundMatrix()

void SkSL::MetalCodeGenerator::writeConstructorCompoundMatrix ( const ConstructorCompound c,
Precedence  parentPrecedence 
)
protected

Definition at line 1615 of file SkSLMetalCodeGenerator.cpp.

1616 {
1617 SkASSERT(c.type().isMatrix());
1618
1619 // Emit and invoke a matrix-constructor helper method if one is necessary.
1620 if (this->matrixConstructHelperIsNeeded(c)) {
1621 this->write(this->getMatrixConstructHelper(c));
1622 this->write("(");
1623 const char* separator = "";
1624 for (const std::unique_ptr<Expression>& expr : c.arguments()) {
1625 this->write(separator);
1626 separator = ", ";
1627 this->writeExpression(*expr, Precedence::kSequence);
1628 }
1629 this->write(")");
1630 return;
1631 }
1632
1633 // Metal doesn't allow creating matrices by passing in scalars and vectors in a jumble; it
1634 // requires your scalars to be grouped up into columns. Because `matrixConstructHelperIsNeeded`
1635 // returned false, we know that none of our scalars/vectors "wrap" across across a column, so we
1636 // can group our inputs up and synthesize a constructor for each column.
1637 const Type& matrixType = c.type();
1638 const Type& columnType = matrixType.columnType(fContext);
1639
1640 this->writeType(matrixType);
1641 this->write("(");
1642 const char* separator = "";
1643 int scalarCount = 0;
1644 for (const std::unique_ptr<Expression>& arg : c.arguments()) {
1645 this->write(separator);
1646 separator = ", ";
1647 if (arg->type().columns() < matrixType.rows()) {
1648 // Write a `floatN(` constructor to group scalars and smaller vectors together.
1649 if (!scalarCount) {
1650 this->writeType(columnType);
1651 this->write("(");
1652 }
1653 scalarCount += arg->type().columns();
1654 }
1655 this->writeExpression(*arg, Precedence::kSequence);
1656 if (scalarCount && scalarCount == matrixType.rows()) {
1657 // Close our `floatN(...` constructor block from above.
1658 this->write(")");
1659 scalarCount = 0;
1660 }
1661 }
1662 this->write(")");
1663}
bool matrixConstructHelperIsNeeded(const ConstructorCompound &c)
std::string getMatrixConstructHelper(const AnyConstructor &c)

◆ writeConstructorCompoundVector()

void SkSL::MetalCodeGenerator::writeConstructorCompoundVector ( const ConstructorCompound c,
Precedence  parentPrecedence 
)
protected

Definition at line 1595 of file SkSLMetalCodeGenerator.cpp.

1596 {
1597 SkASSERT(c.type().isVector());
1598
1599 // Metal supports constructing vectors from a mix of scalars and vectors, but not matrices.
1600 // GLSL supports vec4(mat2x2), so we detect that case here and emit a helper function.
1601 if (c.type().columns() == 4 && c.argumentSpan().size() == 1) {
1602 const Expression& expr = *c.argumentSpan().front();
1603 if (expr.type().isMatrix()) {
1604 this->write(this->getVectorFromMat2x2ConstructorHelper(expr.type()));
1605 this->write("(");
1606 this->writeExpression(expr, Precedence::kSequence);
1607 this->write(")");
1608 return;
1609 }
1610 }
1611
1612 this->writeAnyConstructor(c, "(", ")", parentPrecedence);
1613}
std::string getVectorFromMat2x2ConstructorHelper(const Type &matrixType)

◆ writeConstructorMatrixResize()

void SkSL::MetalCodeGenerator::writeConstructorMatrixResize ( const ConstructorMatrixResize c,
Precedence  parentPrecedence 
)
protected

Definition at line 1523 of file SkSLMetalCodeGenerator.cpp.

1524 {
1525 // Matrix-resize via casting doesn't natively exist in Metal at all, so we always need to use a
1526 // matrix-construct helper here.
1527 this->write(this->getMatrixConstructHelper(c));
1528 this->write("(");
1529 this->writeExpression(*c.argument(), Precedence::kSequence);
1530 this->write(")");
1531}

◆ writeDoStatement()

void SkSL::MetalCodeGenerator::writeDoStatement ( const DoStatement d)
protected

Definition at line 2895 of file SkSLMetalCodeGenerator.cpp.

2895 {
2896 this->write("do ");
2897 this->writeStatement(*d.statement());
2898 this->write(" while (");
2899 this->writeExpression(*d.test(), Precedence::kExpression);
2900 this->write(");");
2901}
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19

◆ writeEqualityHelpers()

void SkSL::MetalCodeGenerator::writeEqualityHelpers ( const Type leftType,
const Type rightType 
)
protected

Definition at line 2078 of file SkSLMetalCodeGenerator.cpp.

2078 {
2079 if (leftType.isArray() && rightType.isArray()) {
2080 this->writeArrayEqualityHelpers(leftType);
2081 return;
2082 }
2083 if (leftType.isStruct() && rightType.isStruct()) {
2084 this->writeStructEqualityHelpers(leftType);
2085 return;
2086 }
2087 if (leftType.isMatrix() && rightType.isMatrix()) {
2088 this->writeMatrixEqualityHelpers(leftType, rightType);
2089 return;
2090 }
2091}
void writeArrayEqualityHelpers(const Type &type)
void writeMatrixEqualityHelpers(const Type &left, const Type &right)
void writeStructEqualityHelpers(const Type &type)

◆ writeExpression()

void SkSL::MetalCodeGenerator::writeExpression ( const Expression expr,
Precedence  parentPrecedence 
)
protected

Definition at line 478 of file SkSLMetalCodeGenerator.cpp.

478 {
479 switch (expr.kind()) {
480 case Expression::Kind::kBinary:
481 this->writeBinaryExpression(expr.as<BinaryExpression>(), parentPrecedence);
482 break;
483 case Expression::Kind::kConstructorArray:
484 case Expression::Kind::kConstructorStruct:
485 this->writeAnyConstructor(expr.asAnyConstructor(), "{", "}", parentPrecedence);
486 break;
487 case Expression::Kind::kConstructorArrayCast:
488 this->writeConstructorArrayCast(expr.as<ConstructorArrayCast>(), parentPrecedence);
489 break;
490 case Expression::Kind::kConstructorCompound:
491 this->writeConstructorCompound(expr.as<ConstructorCompound>(), parentPrecedence);
492 break;
493 case Expression::Kind::kConstructorDiagonalMatrix:
494 case Expression::Kind::kConstructorSplat:
495 this->writeAnyConstructor(expr.asAnyConstructor(), "(", ")", parentPrecedence);
496 break;
497 case Expression::Kind::kConstructorMatrixResize:
498 this->writeConstructorMatrixResize(expr.as<ConstructorMatrixResize>(),
499 parentPrecedence);
500 break;
501 case Expression::Kind::kConstructorScalarCast:
502 case Expression::Kind::kConstructorCompoundCast:
503 this->writeCastConstructor(expr.asAnyConstructor(), "(", ")", parentPrecedence);
504 break;
505 case Expression::Kind::kEmpty:
506 this->write("false");
507 break;
508 case Expression::Kind::kFieldAccess:
509 this->writeFieldAccess(expr.as<FieldAccess>());
510 break;
511 case Expression::Kind::kLiteral:
512 this->writeLiteral(expr.as<Literal>());
513 break;
514 case Expression::Kind::kFunctionCall:
515 this->writeFunctionCall(expr.as<FunctionCall>());
516 break;
517 case Expression::Kind::kPrefix:
518 this->writePrefixExpression(expr.as<PrefixExpression>(), parentPrecedence);
519 break;
520 case Expression::Kind::kPostfix:
521 this->writePostfixExpression(expr.as<PostfixExpression>(), parentPrecedence);
522 break;
523 case Expression::Kind::kSetting:
524 this->writeExpression(*expr.as<Setting>().toLiteral(fCaps), parentPrecedence);
525 break;
526 case Expression::Kind::kSwizzle:
527 this->writeSwizzle(expr.as<Swizzle>());
528 break;
529 case Expression::Kind::kVariableReference:
530 this->writeVariableReference(expr.as<VariableReference>());
531 break;
532 case Expression::Kind::kTernary:
533 this->writeTernaryExpression(expr.as<TernaryExpression>(), parentPrecedence);
534 break;
535 case Expression::Kind::kIndex:
536 this->writeIndexExpression(expr.as<IndexExpression>());
537 break;
538 default:
539 SkDEBUGFAILF("unsupported expression: %s", expr.description().c_str());
540 break;
541 }
542}
const ShaderCaps & fCaps
void writeConstructorMatrixResize(const ConstructorMatrixResize &c, Precedence parentPrecedence)
void writeSwizzle(const Swizzle &swizzle)
void writePrefixExpression(const PrefixExpression &p, Precedence parentPrecedence)
void writeIndexExpression(const IndexExpression &expr)
void writeCastConstructor(const AnyConstructor &c, const char *leftBracket, const char *rightBracket, Precedence parentPrecedence)
void writeTernaryExpression(const TernaryExpression &t, Precedence parentPrecedence)
void writeVariableReference(const VariableReference &ref)
void writeFunctionCall(const FunctionCall &c)
void writeConstructorArrayCast(const ConstructorArrayCast &c, Precedence parentPrecedence)
void writeLiteral(const Literal &f)
void writePostfixExpression(const PostfixExpression &p, Precedence parentPrecedence)
void writeBinaryExpression(const BinaryExpression &b, Precedence parentPrecedence)
void writeFieldAccess(const FieldAccess &f)
void writeConstructorCompound(const ConstructorCompound &c, Precedence parentPrecedence)

◆ writeExpressionStatement()

void SkSL::MetalCodeGenerator::writeExpressionStatement ( const ExpressionStatement s)
protected

Definition at line 2903 of file SkSLMetalCodeGenerator.cpp.

2903 {
2904 if (fProgram.fConfig->fSettings.fOptimize && !Analysis::HasSideEffects(*s.expression())) {
2905 // Don't emit dead expressions.
2906 return;
2907 }
2908 this->writeExpression(*s.expression(), Precedence::kStatement);
2909 this->write(";");
2910}
bool HasSideEffects(const Expression &expr)

◆ writeExtension()

void SkSL::MetalCodeGenerator::writeExtension ( const Extension ext)
protected

Definition at line 418 of file SkSLMetalCodeGenerator.cpp.

418 {
419 this->writeLine("#extension " + std::string(ext.name()) + " : enable");
420}

◆ writeFieldAccess()

void SkSL::MetalCodeGenerator::writeFieldAccess ( const FieldAccess f)
protected

Definition at line 1868 of file SkSLMetalCodeGenerator.cpp.

1868 {
1869 const Field* field = &f.base()->type().fields()[f.fieldIndex()];
1870 if (FieldAccess::OwnerKind::kDefault == f.ownerKind()) {
1871 this->writeExpression(*f.base(), Precedence::kPostfix);
1872 this->write(".");
1873 }
1874 switch (field->fLayout.fBuiltin) {
1876 this->write("_out.sk_Position");
1877 break;
1879 this->write("_out.sk_PointSize");
1880 break;
1881 default:
1882 if (FieldAccess::OwnerKind::kAnonymousInterfaceBlock == f.ownerKind()) {
1883 this->write("_globals.");
1884 this->write(fInterfaceBlockNameMap[&f.base()->type()]);
1885 this->write("->");
1886 }
1887 this->writeName(field->fName);
1888 }
1889}
constexpr int SK_POSITION_BUILTIN
constexpr int SK_POINTSIZE_BUILTIN

◆ writeFields()

void SkSL::MetalCodeGenerator::writeFields ( SkSpan< const Field fields,
Position  pos 
)
protected

Definition at line 2708 of file SkSLMetalCodeGenerator.cpp.

2708 {
2709 MemoryLayout memoryLayout(MemoryLayout::Standard::kMetal);
2710 int currentOffset = 0;
2711 for (const Field& field : fields) {
2712 int fieldOffset = field.fLayout.fOffset;
2713 const Type* fieldType = field.fType;
2714 if (!memoryLayout.isSupported(*fieldType)) {
2715 fContext.fErrors->error(parentPos, "type '" + std::string(fieldType->name()) +
2716 "' is not permitted here");
2717 return;
2718 }
2719 if (fieldOffset != -1) {
2720 if (currentOffset > fieldOffset) {
2721 fContext.fErrors->error(field.fPosition,
2722 "offset of field '" + std::string(field.fName) +
2723 "' must be at least " + std::to_string(currentOffset));
2724 return;
2725 } else if (currentOffset < fieldOffset) {
2726 this->write("char pad");
2727 this->write(std::to_string(fPaddingCount++));
2728 this->write("[");
2729 this->write(std::to_string(fieldOffset - currentOffset));
2730 this->writeLine("];");
2731 currentOffset = fieldOffset;
2732 }
2733 int alignment = memoryLayout.alignment(*fieldType);
2734 if (fieldOffset % alignment) {
2735 fContext.fErrors->error(field.fPosition,
2736 "offset of field '" + std::string(field.fName) +
2737 "' must be a multiple of " + std::to_string(alignment));
2738 return;
2739 }
2740 }
2741 if (fieldType->isUnsizedArray()) {
2742 // An unsized array always appears as the last member of a storage block. We declare
2743 // it as a one-element array and allow dereferencing past the capacity.
2744 // TODO(armansito): This is because C++ does not support flexible array members like C99
2745 // does. This generally works but it can lead to UB as compilers are free to insert
2746 // padding past the first element of the array. An alternative approach is to declare
2747 // the struct without the unsized array member and replace variable references with a
2748 // buffer offset calculation based on sizeof().
2749 this->writeModifiers(field.fModifierFlags);
2750 this->writeType(fieldType->componentType());
2751 this->write(" ");
2752 this->writeName(field.fName);
2753 this->write("[1]");
2754 } else {
2755 size_t fieldSize = memoryLayout.size(*fieldType);
2756 if (fieldSize > static_cast<size_t>(std::numeric_limits<int>::max() - currentOffset)) {
2757 fContext.fErrors->error(parentPos, "field offset overflow");
2758 return;
2759 }
2760 currentOffset += fieldSize;
2761 this->writeModifiers(field.fModifierFlags);
2762 this->writeType(*fieldType);
2763 this->write(" ");
2764 this->writeName(field.fName);
2765 }
2766 this->writeLine(";");
2767 }
2768}
int alignment(const Type *type, bool isPacked) const
void writeModifiers(ModifierFlags flags)

◆ writeForStatement()

void SkSL::MetalCodeGenerator::writeForStatement ( const ForStatement f)
protected

Definition at line 2868 of file SkSLMetalCodeGenerator.cpp.

2868 {
2869 // Emit loops of the form 'for(;test;)' as 'while(test)', which is probably how they started
2870 if (!f.initializer() && f.test() && !f.next()) {
2871 this->write("while (");
2872 this->writeExpression(*f.test(), Precedence::kExpression);
2873 this->write(") ");
2874 this->writeStatement(*f.statement());
2875 return;
2876 }
2877
2878 this->write("for (");
2879 if (f.initializer() && !f.initializer()->isEmpty()) {
2880 this->writeStatement(*f.initializer());
2881 } else {
2882 this->write("; ");
2883 }
2884 if (f.test()) {
2885 this->writeExpression(*f.test(), Precedence::kExpression);
2886 }
2887 this->write("; ");
2888 if (f.next()) {
2889 this->writeExpression(*f.next(), Precedence::kExpression);
2890 }
2891 this->write(") ");
2892 this->writeStatement(*f.statement());
2893}

◆ writeFragCoord()

void SkSL::MetalCodeGenerator::writeFragCoord ( )
protected

Definition at line 1687 of file SkSLMetalCodeGenerator.cpp.

1687 {
1688 if (!fRTFlipName.empty()) {
1689 this->write("float4(_fragCoord.x, ");
1690 this->write(fRTFlipName.c_str());
1691 this->write(".x + ");
1692 this->write(fRTFlipName.c_str());
1693 this->write(".y * _fragCoord.y, 0.0, _fragCoord.w)");
1694 } else {
1695 this->write("float4(_fragCoord.x, _fragCoord.y, 0.0, _fragCoord.w)");
1696 }
1697}

◆ writeFunction()

void SkSL::MetalCodeGenerator::writeFunction ( const FunctionDefinition f)
protected

Definition at line 2613 of file SkSLMetalCodeGenerator.cpp.

2613 {
2614 SkASSERT(!fProgram.fConfig->fSettings.fFragColorIsInOut);
2615
2616 if (!this->writeFunctionDeclaration(f.declaration())) {
2617 return;
2618 }
2619
2620 fCurrentFunction = &f.declaration();
2621 SkScopeExit clearCurrentFunction([&] { fCurrentFunction = nullptr; });
2622
2623 this->writeLine(" {");
2624
2625 if (f.declaration().isMain()) {
2626 fIndentation++;
2627 this->writeGlobalInit();
2629 this->writeThreadgroupInit();
2630 this->writeComputeMainInputs();
2631 }
2632 else {
2633 this->writeLine("Outputs _out;");
2634 this->writeLine("(void)_out;");
2635 }
2636 fIndentation--;
2637 }
2638
2639 fFunctionHeader.clear();
2640 StringStream buffer;
2641 {
2642 AutoOutputStream outputToBuffer(this, &buffer);
2643 fIndentation++;
2644 for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) {
2645 if (!stmt->isEmpty()) {
2646 this->writeStatement(*stmt);
2647 this->finishLine();
2648 }
2649 }
2650 if (f.declaration().isMain()) {
2651 // If the main function doesn't end with a return, we need to synthesize one here.
2652 if (!is_block_ending_with_return(f.body().get())) {
2654 this->finishLine();
2655 }
2656 }
2657 fIndentation--;
2658 this->writeLine("}");
2659 }
2660 this->write(fFunctionHeader);
2661 this->write(buffer.str());
2662}
const FunctionDeclaration * fCurrentFunction
static const uint8_t buffer[]
static bool is_block_ending_with_return(const Statement *stmt)

◆ writeFunctionCall()

void SkSL::MetalCodeGenerator::writeFunctionCall ( const FunctionCall c)
protected

Definition at line 593 of file SkSLMetalCodeGenerator.cpp.

593 {
594 const FunctionDeclaration& function = c.function();
595
596 // Many intrinsics need to be rewritten in Metal.
597 if (function.isIntrinsic()) {
598 if (this->writeIntrinsicCall(c, function.intrinsicKind())) {
599 return;
600 }
601 }
602
603 // Look for out parameters. SkSL guarantees GLSL's out-param semantics, and we need to emulate
604 // it if an out-param is encountered. (Specifically, out-parameters in GLSL are only written
605 // back to the original variable at the end of the function call; also, swizzles are supported,
606 // whereas Metal doesn't allow a swizzle to be passed to a `floatN&`.)
607 const ExpressionArray& arguments = c.arguments();
608 SkSpan<Variable* const> parameters = function.parameters();
609 SkASSERT(SkToSizeT(arguments.size()) == parameters.size());
610
611 bool foundOutParam = false;
612 STArray<16, std::string> scratchVarName;
613 scratchVarName.push_back_n(arguments.size(), std::string());
614
615 for (int index = 0; index < arguments.size(); ++index) {
616 // If this is an out parameter...
617 if (parameters[index]->modifierFlags() & ModifierFlag::kOut) {
618 // Assignability was verified at IRGeneration time, so this should always succeed.
619 [[maybe_unused]] Analysis::AssignmentInfo info;
620 SkASSERT(Analysis::IsAssignable(*arguments[index], &info));
621
622 scratchVarName[index] = this->getTempVariable(arguments[index]->type());
623 foundOutParam = true;
624 }
625 }
626
627 if (foundOutParam) {
628 // Out parameters need to be written back to at the end of the function. To do this, we
629 // generate a comma-separated sequence expression that copies the out-param expressions into
630 // our temporary variables, calls the original function--storing its result into a scratch
631 // variable--and then writes the temp variables back into the original out params using the
632 // original out-param expressions. This would look something like:
633 //
634 // ((_skResult = func((_skTemp = myOutParam.x), 123)), (myOutParam.x = _skTemp), _skResult)
635 // ^ ^ ^ ^
636 // return value passes copy of argument copies back into argument return value
637 //
638 // While these expressions are complex, they allow us to maintain the proper sequencing that
639 // is necessary for out-parameters, as well as allowing us to support things like swizzles
640 // and array indices which Metal references cannot natively handle.
641
642 // We will be emitting inout expressions twice, so it's important to enable index
643 // substitution in case we encounter any side-effecting indexes.
645 this->write("((");
646
647 // ((_skResult =
648 std::string scratchResultName;
649 if (!function.returnType().isVoid()) {
650 scratchResultName = this->getTempVariable(c.type());
651 this->write(scratchResultName);
652 this->write(" = ");
653 }
654
655 // ((_skResult = func(
656 this->write(function.mangledName());
657 this->write("(");
658
659 // ((_skResult = func((_skTemp = myOutParam.x), 123
660 const char* separator = "";
661 this->writeFunctionRequirementArgs(function, separator);
662
663 for (int i = 0; i < arguments.size(); ++i) {
664 this->write(separator);
665 separator = ", ";
666 if (parameters[i]->modifierFlags() & ModifierFlag::kOut) {
667 SkASSERT(!scratchVarName[i].empty());
668 if (parameters[i]->modifierFlags() & ModifierFlag::kIn) {
669 // `inout` parameters initialize the scratch variable with the passed-in
670 // argument's value.
671 this->write("(");
672 this->write(scratchVarName[i]);
673 this->write(" = ");
674 this->writeExpression(*arguments[i], Precedence::kAssignment);
675 this->write(")");
676 } else {
677 // `out` parameters pass a reference to the uninitialized scratch variable.
678 this->write(scratchVarName[i]);
679 }
680 } else {
681 // Regular parameters are passed as-is.
682 this->writeExpression(*arguments[i], Precedence::kSequence);
683 }
684 }
685
686 // ((_skResult = func((_skTemp = myOutParam.x), 123))
687 this->write("))");
688
689 // ((_skResult = func((_skTemp = myOutParam.x), 123)), (myOutParam.x = _skTemp)
690 for (int i = 0; i < arguments.size(); ++i) {
691 if (!scratchVarName[i].empty()) {
692 this->write(", (");
693 this->writeExpression(*arguments[i], Precedence::kAssignment);
694 this->write(" = ");
695 this->write(scratchVarName[i]);
696 this->write(")");
697 }
698 }
699
700 // ((_skResult = func((_skTemp = myOutParam.x), 123)), (myOutParam.x = _skTemp),
701 // _skResult
702 if (!scratchResultName.empty()) {
703 this->write(", ");
704 this->write(scratchResultName);
705 }
706
707 // ((_skResult = func((_skTemp = myOutParam.x), 123)), (myOutParam.x = _skTemp),
708 // _skResult)
709 this->write(")");
710 });
711 } else {
712 // Emit the function call as-is, only prepending the required arguments.
713 this->write(function.mangledName());
714 this->write("(");
715 const char* separator = "";
716 this->writeFunctionRequirementArgs(function, separator);
717 for (int i = 0; i < arguments.size(); ++i) {
718 SkASSERT(scratchVarName[i].empty());
719 this->write(separator);
720 separator = ", ";
721 this->writeExpression(*arguments[i], Precedence::kSequence);
722 }
723 this->write(")");
724 }
725}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
constexpr size_t SkToSizeT(S x)
Definition SkTo.h:31
bool writeIntrinsicCall(const FunctionCall &c, IntrinsicKind kind)
std::string getTempVariable(const Type &varType)
void writeFunctionRequirementArgs(const FunctionDeclaration &f, const char *&separator)
constexpr size_t size() const
Definition SkSpan_impl.h:95
T * push_back_n(int n)
Definition SkTArray.h:262
EMSCRIPTEN_KEEPALIVE void empty()
Dart_NativeFunction function
Definition fuchsia.cc:51
bool IsAssignable(Expression &expr, AssignmentInfo *info=nullptr, ErrorReporter *errors=nullptr)

◆ writeFunctionDeclaration()

bool SkSL::MetalCodeGenerator::writeFunctionDeclaration ( const FunctionDeclaration f)
protected

Definition at line 2400 of file SkSLMetalCodeGenerator.cpp.

2400 {
2402 ? "_globals._anonInterface0->" SKSL_RTFLIP_NAME
2403 : "";
2404 const char* separator = "";
2405 if (f.isMain()) {
2407 this->write("fragment Outputs fragmentMain(");
2408 } else if (ProgramConfig::IsVertex(fProgram.fConfig->fKind)) {
2409 this->write("vertex Outputs vertexMain(");
2410 } else if (ProgramConfig::IsCompute(fProgram.fConfig->fKind)) {
2411 this->write("kernel void computeMain(");
2412 } else {
2413 fContext.fErrors->error(Position(), "unsupported kind of program");
2414 return false;
2415 }
2417 this->write("Inputs _in [[stage_in]]");
2418 separator = ", ";
2419 }
2420 if (-1 != fUniformBuffer) {
2421 this->write(separator);
2422 this->write("constant Uniforms& _uniforms [[buffer(" +
2423 std::to_string(fUniformBuffer) + ")]]");
2424 separator = ", ";
2425 }
2426 for (const ProgramElement* e : fProgram.elements()) {
2427 if (e->is<GlobalVarDeclaration>()) {
2428 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
2429 const VarDeclaration& decl = decls.varDeclaration();
2430 const Variable* var = decl.var();
2431 const SkSL::Type::TypeKind varKind = var->type().typeKind();
2432
2433 if (varKind == Type::TypeKind::kSampler || varKind == Type::TypeKind::kTexture) {
2434 if (var->type().dimensions() != SpvDim2D) {
2435 // Not yet implemented--Skia currently only uses 2D textures.
2436 fContext.fErrors->error(decls.fPosition, "Unsupported texture dimensions");
2437 return false;
2438 }
2439
2440 int binding = getUniformBinding(var->layout());
2441 this->write(separator);
2442 separator = ", ";
2443
2444 if (varKind == Type::TypeKind::kSampler) {
2445 this->writeType(var->type().textureType());
2446 this->write(" ");
2447 this->writeName(var->mangledName());
2448 this->write(kTextureSuffix);
2449 this->write(" [[texture(");
2450 this->write(std::to_string(binding));
2451 this->write(")]], sampler ");
2452 this->writeName(var->mangledName());
2453 this->write(kSamplerSuffix);
2454 this->write(" [[sampler(");
2455 this->write(std::to_string(binding));
2456 this->write(")]]");
2457 } else {
2459 this->writeType(var->type());
2460 this->write(" ");
2461 this->writeName(var->mangledName());
2462 this->write(" [[texture(");
2463 this->write(std::to_string(binding));
2464 this->write(")]]");
2465 }
2466 } else if (ProgramConfig::IsCompute(fProgram.fConfig->fKind)) {
2467 std::string_view attr;
2468 switch (var->layout().fBuiltin) {
2470 attr = " [[threadgroups_per_grid]]";
2471 break;
2473 attr = " [[threadgroup_position_in_grid]]";
2474 break;
2476 attr = " [[thread_position_in_threadgroup]]";
2477 break;
2479 attr = " [[thread_position_in_grid]]";
2480 break;
2482 attr = " [[thread_index_in_threadgroup]]";
2483 break;
2484 default:
2485 break;
2486 }
2487 if (!attr.empty()) {
2488 this->write(separator);
2489 this->writeType(var->type());
2490 this->write(" ");
2491 this->write(var->name());
2492 this->write(attr);
2493 separator = ", ";
2494 }
2495 }
2496 } else if (e->is<InterfaceBlock>()) {
2497 const InterfaceBlock& intf = e->as<InterfaceBlock>();
2498 if (intf.typeName() == "sk_PerVertex") {
2499 continue;
2500 }
2501 this->write(separator);
2502 if (is_readonly(intf)) {
2503 this->write("const ");
2504 }
2505 this->write(is_buffer(intf) ? "device " : "constant ");
2506 this->writeType(intf.var()->type());
2507 this->write("& " );
2508 this->write(fInterfaceBlockNameMap[&intf.var()->type()]);
2509 this->write(" [[buffer(");
2510 this->write(std::to_string(this->getUniformBinding(intf.var()->layout())));
2511 this->write(")]]");
2512 separator = ", ";
2513 }
2514 }
2517 fInterfaceBlockNameMap.empty()) {
2518 this->write(separator);
2519 this->write("constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]]");
2520 fRTFlipName = "_anonInterface0." SKSL_RTFLIP_NAME;
2521 separator = ", ";
2522 }
2523 this->write(separator);
2524 this->write("bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]");
2525 if (this->requirements(f) & kSampleMaskIn_Requirement) {
2526 this->write(", uint sk_SampleMaskIn [[sample_mask]]");
2527 }
2529 this->write(", half4 " + std::string(fCaps.fFBFetchColorName) +
2530 " [[color(0)]]\n");
2531 }
2532 separator = ", ";
2533 } else if (ProgramConfig::IsVertex(fProgram.fConfig->fKind)) {
2534 this->write(separator);
2535 this->write("uint sk_VertexID [[vertex_id]], uint sk_InstanceID [[instance_id]]");
2536 separator = ", ";
2537 }
2538 } else {
2539 this->writeType(f.returnType());
2540 this->write(" ");
2541 this->writeName(f.mangledName());
2542 this->write("(");
2543 this->writeFunctionRequirementParams(f, separator);
2544 }
2545 for (const Variable* param : f.parameters()) {
2546 // This is a workaround for our test files. They use the runtime effect signature, so main
2547 // takes a coords parameter. We detect these at IR generation time, and we omit them from
2548 // the declaration here, so the function is valid Metal. (Well, valid as long as the
2549 // coordinates aren't actually referenced.)
2550 if (f.isMain() && param == f.getMainCoordsParameter()) {
2551 continue;
2552 }
2553 this->write(separator);
2554 separator = ", ";
2555 this->writeModifiers(param->modifierFlags());
2556 this->writeType(param->type());
2557 if (pass_by_reference(param->type(), param->modifierFlags())) {
2558 this->write("&");
2559 }
2560 this->write(" ");
2561 this->writeName(param->mangledName());
2562 }
2563 this->write(")");
2564 return true;
2565}
constexpr int SK_WORKGROUPID_BUILTIN
constexpr int SK_GLOBALINVOCATIONID_BUILTIN
constexpr int SK_LOCALINVOCATIONID_BUILTIN
constexpr int SK_NUMWORKGROUPS_BUILTIN
constexpr int SK_LOCALINVOCATIONINDEX_BUILTIN
#define SKSL_RTFLIP_NAME
Definition SkSLProgram.h:19
static constexpr char kSamplerSuffix[]
static constexpr char kTextureSuffix[]
static constexpr Requirements kSampleMaskIn_Requirement
void writeFunctionRequirementParams(const FunctionDeclaration &f, const char *&separator)
int getUniformBinding(const Layout &layout)
static bool is_buffer(const InterfaceBlock &block)
static bool is_readonly(const InterfaceBlock &block)
static bool pass_by_reference(const Type &type, ModifierFlags flags)
static bool IsVertex(ProgramKind kind)
static bool IsFragment(ProgramKind kind)
ProgramInterface fInterface
const char * fFBFetchColorName
Definition SkSLUtil.h:158

◆ writeFunctionPrototype()

void SkSL::MetalCodeGenerator::writeFunctionPrototype ( const FunctionPrototype f)
protected

Definition at line 2567 of file SkSLMetalCodeGenerator.cpp.

2567 {
2568 this->writeFunctionDeclaration(f.declaration());
2569 this->writeLine(";");
2570}

◆ writeFunctionRequirementArgs()

void SkSL::MetalCodeGenerator::writeFunctionRequirementArgs ( const FunctionDeclaration f,
const char *&  separator 
)
protected

Definition at line 2290 of file SkSLMetalCodeGenerator.cpp.

2291 {
2294 this->write(separator);
2295 this->write("_in");
2296 separator = ", ";
2297 }
2299 this->write(separator);
2300 this->write("_out");
2301 separator = ", ";
2302 }
2304 this->write(separator);
2305 this->write("_uniforms");
2306 separator = ", ";
2307 }
2309 this->write(separator);
2310 this->write("_globals");
2311 separator = ", ";
2312 }
2314 this->write(separator);
2315 this->write("_fragCoord");
2316 separator = ", ";
2317 }
2319 this->write(separator);
2320 this->write("sk_SampleMaskIn");
2321 separator = ", ";
2322 }
2324 this->write(separator);
2325 this->write("sk_VertexID");
2326 separator = ", ";
2327 }
2329 this->write(separator);
2330 this->write("sk_InstanceID");
2331 separator = ", ";
2332 }
2334 this->write(separator);
2335 this->write("_threadgroups");
2336 separator = ", ";
2337 }
2338}
static constexpr Requirements kFragCoord_Requirement
static constexpr Requirements kGlobals_Requirement
static constexpr Requirements kVertexID_Requirement
static constexpr Requirements kInputs_Requirement
static constexpr Requirements kOutputs_Requirement
static constexpr Requirements kUniforms_Requirement
static constexpr Requirements kInstanceID_Requirement
static constexpr Requirements kThreadgroups_Requirement

◆ writeFunctionRequirementParams()

void SkSL::MetalCodeGenerator::writeFunctionRequirementParams ( const FunctionDeclaration f,
const char *&  separator 
)
protected

Definition at line 2340 of file SkSLMetalCodeGenerator.cpp.

2341 {
2344 this->write(separator);
2345 this->write("Inputs _in");
2346 separator = ", ";
2347 }
2349 this->write(separator);
2350 this->write("thread Outputs& _out");
2351 separator = ", ";
2352 }
2354 this->write(separator);
2355 this->write("Uniforms _uniforms");
2356 separator = ", ";
2357 }
2359 this->write(separator);
2360 this->write("thread Globals& _globals");
2361 separator = ", ";
2362 }
2364 this->write(separator);
2365 this->write("float4 _fragCoord");
2366 separator = ", ";
2367 }
2369 this->write(separator);
2370 this->write("uint sk_SampleMaskIn");
2371 separator = ", ";
2372 }
2374 this->write(separator);
2375 this->write("uint sk_VertexID");
2376 separator = ", ";
2377 }
2379 this->write(separator);
2380 this->write("uint sk_InstanceID");
2381 separator = ", ";
2382 }
2384 this->write(separator);
2385 this->write("threadgroup Threadgroups& _threadgroups");
2386 separator = ", ";
2387 }
2388}

◆ writeGlobalInit()

void SkSL::MetalCodeGenerator::writeGlobalInit ( )
protected

Definition at line 3288 of file SkSLMetalCodeGenerator.cpp.

3288 {
3289 class : public GlobalStructVisitor {
3290 public:
3291 void visitInterfaceBlock(const InterfaceBlock& blockType,
3292 std::string_view blockName) override {
3293 this->addElement();
3294 fCodeGen->write("&");
3295 fCodeGen->writeName(blockName);
3296 }
3297 void visitTexture(const Type&, std::string_view name) override {
3298 this->addElement();
3299 fCodeGen->writeName(name);
3300 }
3301 void visitSampler(const Type&, std::string_view name) override {
3302 this->addElement();
3303 fCodeGen->write("{");
3304 fCodeGen->writeName(name);
3305 fCodeGen->write(kTextureSuffix);
3306 fCodeGen->write(", ");
3307 fCodeGen->writeName(name);
3308 fCodeGen->write(kSamplerSuffix);
3309 fCodeGen->write("}");
3310 }
3311 void visitConstantVariable(const VarDeclaration& decl) override {
3312 // Constant-expression variables aren't put in the global struct.
3313 }
3314 void visitNonconstantVariable(const Variable& var, const Expression* value) override {
3315 this->addElement();
3316 if (value) {
3317 fCodeGen->writeVarInitializer(var, *value);
3318 } else {
3319 fCodeGen->write("{}");
3320 }
3321 }
3322 void addElement() {
3323 if (fFirst) {
3324 fCodeGen->write("Globals _globals{");
3325 fFirst = false;
3326 } else {
3327 fCodeGen->write(", ");
3328 }
3329 }
3330 void finish() {
3331 if (!fFirst) {
3332 fCodeGen->writeLine("};");
3333 fCodeGen->writeLine("(void)_globals;");
3334 }
3335 }
3336 MetalCodeGenerator* fCodeGen = nullptr;
3337 bool fFirst = true;
3338 } visitor;
3339
3340 visitor.fCodeGen = this;
3341 this->visitGlobalStruct(&visitor);
3342 visitor.finish();
3343}

◆ writeGlobalStruct()

void SkSL::MetalCodeGenerator::writeGlobalStruct ( )
protected

Definition at line 3224 of file SkSLMetalCodeGenerator.cpp.

3224 {
3225 class : public GlobalStructVisitor {
3226 public:
3227 void visitInterfaceBlock(const InterfaceBlock& block,
3228 std::string_view blockName) override {
3229 this->addElement();
3230 fCodeGen->write(" ");
3231 if (is_readonly(block)) {
3232 fCodeGen->write("const ");
3233 }
3234 fCodeGen->write(is_buffer(block) ? "device " : "constant ");
3235 fCodeGen->write(block.typeName());
3236 fCodeGen->write("* ");
3237 fCodeGen->writeName(blockName);
3238 fCodeGen->write(";\n");
3239 }
3240 void visitTexture(const Type& type, std::string_view name) override {
3241 this->addElement();
3242 fCodeGen->write(" ");
3243 fCodeGen->writeType(type);
3244 fCodeGen->write(" ");
3245 fCodeGen->writeName(name);
3246 fCodeGen->write(";\n");
3247 }
3248 void visitSampler(const Type&, std::string_view name) override {
3249 this->addElement();
3250 fCodeGen->write(" sampler2D ");
3251 fCodeGen->writeName(name);
3252 fCodeGen->write(";\n");
3253 }
3254 void visitConstantVariable(const VarDeclaration& decl) override {
3255 // Constants aren't added to the global struct.
3256 }
3257 void visitNonconstantVariable(const Variable& var, const Expression* value) override {
3258 this->addElement();
3259 fCodeGen->write(" ");
3260 fCodeGen->writeModifiers(var.modifierFlags());
3261 fCodeGen->writeType(var.type());
3262 fCodeGen->write(" ");
3263 fCodeGen->writeName(var.mangledName());
3264 fCodeGen->write(";\n");
3265 }
3266 void addElement() {
3267 if (fFirst) {
3268 fCodeGen->write("struct Globals {\n");
3269 fFirst = false;
3270 }
3271 }
3272 void finish() {
3273 if (!fFirst) {
3274 fCodeGen->writeLine("};");
3275 fFirst = true;
3276 }
3277 }
3278
3279 MetalCodeGenerator* fCodeGen = nullptr;
3280 bool fFirst = true;
3281 } visitor;
3282
3283 visitor.fCodeGen = this;
3284 this->visitGlobalStruct(&visitor);
3285 visitor.finish();
3286}

◆ writeHeader()

void SkSL::MetalCodeGenerator::writeHeader ( )
protected

Definition at line 2974 of file SkSLMetalCodeGenerator.cpp.

2974 {
2975 this->writeLine("#include <metal_stdlib>");
2976 this->writeLine("#include <simd/simd.h>");
2977 this->writeLine("#ifdef __clang__");
2978 this->writeLine("#pragma clang diagnostic ignored \"-Wall\"");
2979 this->writeLine("#endif");
2980 this->writeLine("using namespace metal;");
2981}

◆ writeIfStatement()

void SkSL::MetalCodeGenerator::writeIfStatement ( const IfStatement stmt)
protected

Definition at line 2857 of file SkSLMetalCodeGenerator.cpp.

2857 {
2858 this->write("if (");
2859 this->writeExpression(*stmt.test(), Precedence::kExpression);
2860 this->write(") ");
2861 this->writeStatement(*stmt.ifTrue());
2862 if (stmt.ifFalse()) {
2863 this->write(" else ");
2864 this->writeStatement(*stmt.ifFalse());
2865 }
2866}

◆ writeIndexExpression()

void SkSL::MetalCodeGenerator::writeIndexExpression ( const IndexExpression expr)
protected

Definition at line 1844 of file SkSLMetalCodeGenerator.cpp.

1844 {
1845 // Metal does not seem to handle assignment into `vec.zyx[i]` properly--it compiles, but the
1846 // results are wrong. We rewrite the expression as `vec[uint3(2,1,0)[i]]` instead. (Filed with
1847 // Apple as FB12055941.)
1848 if (expr.base()->is<Swizzle>() && expr.base()->as<Swizzle>().components().size() > 1) {
1849 const Swizzle& swizzle = expr.base()->as<Swizzle>();
1850 this->writeExpression(*swizzle.base(), Precedence::kPostfix);
1851 this->write("[uint" + std::to_string(swizzle.components().size()) + "(");
1852 auto separator = SkSL::String::Separator();
1853 for (int8_t component : swizzle.components()) {
1854 this->write(separator());
1855 this->write(std::to_string(component));
1856 }
1857 this->write(")[");
1858 this->writeIndexInnerExpression(*expr.index());
1859 this->write("]]");
1860 } else {
1861 this->writeExpression(*expr.base(), Precedence::kPostfix);
1862 this->write("[");
1863 this->writeIndexInnerExpression(*expr.index());
1864 this->write("]");
1865 }
1866}
void writeIndexInnerExpression(const Expression &expr)
std::string void void auto Separator()
Definition SkSLString.h:30

◆ writeIndexInnerExpression()

void SkSL::MetalCodeGenerator::writeIndexInnerExpression ( const Expression expr)
protected

Definition at line 1812 of file SkSLMetalCodeGenerator.cpp.

1812 {
1814 // If this expression already exists in the index-substitution map, use the substitute.
1815 if (const std::string* existing = fIndexSubstitutionData->fMap.find(&expr)) {
1816 this->write(*existing);
1817 return;
1818 }
1819
1820 // If this expression is non-trivial, we will need to create a scratch variable and store
1821 // its value there.
1822 if (fIndexSubstitutionData->fCreateSubstitutes && !Analysis::IsTrivialExpression(expr)) {
1823 // Create a substitute variable and emit it into the main stream.
1824 std::string scratchVar = this->getTempVariable(expr.type());
1825 this->write(scratchVar);
1826
1827 // Initialize the substitute variable in the prefix-stream.
1828 AutoOutputStream outputToPrefixStream(this, &fIndexSubstitutionData->fPrefixStream);
1829 this->write(scratchVar);
1830 this->write(" = ");
1831 this->writeExpression(expr, Precedence::kAssignment);
1832 this->write(", ");
1833
1834 // Remember the substitute variable in our map.
1835 fIndexSubstitutionData->fMap.set(&expr, std::move(scratchVar));
1836 return;
1837 }
1838 }
1839
1840 // We don't require index-substitution; just emit the expression normally.
1841 this->writeExpression(expr, Precedence::kExpression);
1842}
bool IsTrivialExpression(const Expression &expr)

◆ writeInputStruct()

void SkSL::MetalCodeGenerator::writeInputStruct ( )
protected

Definition at line 3055 of file SkSLMetalCodeGenerator.cpp.

3055 {
3056 this->write("struct Inputs {\n");
3057 for (const ProgramElement* e : fProgram.elements()) {
3058 if (e->is<GlobalVarDeclaration>()) {
3059 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
3060 const Variable& var = *decls.varDeclaration().var();
3061 if (is_input(var)) {
3062 this->write(" ");
3064 needs_address_space(var.type(), var.modifierFlags())) {
3065 // TODO: address space support
3066 this->write("device ");
3067 }
3068 this->writeType(var.type());
3069 if (pass_by_reference(var.type(), var.modifierFlags())) {
3070 this->write("&");
3071 }
3072 this->write(" ");
3073 this->writeName(var.mangledName());
3074 if (-1 != var.layout().fLocation) {
3076 this->write(" [[attribute(" + std::to_string(var.layout().fLocation) +
3077 ")]]");
3078 } else if (ProgramConfig::IsFragment(fProgram.fConfig->fKind)) {
3079 this->write(" [[user(locn" + std::to_string(var.layout().fLocation) +
3080 ")]]");
3081 }
3082 }
3083 this->write(";\n");
3084 }
3085 }
3086 }
3087 this->write("};\n");
3088}
static bool needs_address_space(const Type &type, ModifierFlags modifiers)

◆ writeInterfaceBlock()

void SkSL::MetalCodeGenerator::writeInterfaceBlock ( const InterfaceBlock intf)
protected

Definition at line 2676 of file SkSLMetalCodeGenerator.cpp.

2676 {
2677 if (intf.typeName() == "sk_PerVertex") {
2678 return;
2679 }
2680 const Type* structType = &intf.var()->type().componentType();
2681 this->writeModifiers(intf.var()->modifierFlags());
2682 this->write("struct ");
2683 this->writeType(*structType);
2684 this->writeLine(" {");
2685 fIndentation++;
2686 this->writeFields(structType->fields(), structType->fPosition);
2688 this->writeLine("float2 " SKSL_RTFLIP_NAME ";");
2689 }
2690 fIndentation--;
2691 this->write("}");
2692 if (!intf.instanceName().empty()) {
2693 this->write(" ");
2694 this->write(intf.instanceName());
2695 if (intf.arraySize() > 0) {
2696 this->write("[");
2697 this->write(std::to_string(intf.arraySize()));
2698 this->write("]");
2699 }
2700 fInterfaceBlockNameMap.set(&intf.var()->type(), std::string(intf.instanceName()));
2701 } else {
2702 fInterfaceBlockNameMap.set(&intf.var()->type(),
2703 "_anonInterface" + std::to_string(fAnonInterfaceCount++));
2704 }
2705 this->writeLine(";");
2706}
void writeFields(SkSpan< const Field > fields, Position pos)

◆ writeInterfaceBlocks()

void SkSL::MetalCodeGenerator::writeInterfaceBlocks ( )
protected

Definition at line 3148 of file SkSLMetalCodeGenerator.cpp.

3148 {
3149 bool wroteInterfaceBlock = false;
3150 for (const ProgramElement* e : fProgram.elements()) {
3151 if (e->is<InterfaceBlock>()) {
3152 this->writeInterfaceBlock(e->as<InterfaceBlock>());
3153 wroteInterfaceBlock = true;
3154 }
3155 }
3156 if (!wroteInterfaceBlock &&
3158 this->writeLine("struct sksl_synthetic_uniforms {");
3159 this->writeLine(" float2 " SKSL_RTFLIP_NAME ";");
3160 this->writeLine("};");
3161 }
3162}
void writeInterfaceBlock(const InterfaceBlock &intf)

◆ writeIntrinsicCall()

bool SkSL::MetalCodeGenerator::writeIntrinsicCall ( const FunctionCall c,
IntrinsicKind  kind 
)
protected

Definition at line 875 of file SkSLMetalCodeGenerator.cpp.

875 {
876 const ExpressionArray& arguments = c.arguments();
877 switch (kind) {
878 case k_textureRead_IntrinsicKind: {
879 this->writeExpression(*arguments[0], Precedence::kExpression);
880 this->write(".read(");
881 this->writeExpression(*arguments[1], Precedence::kSequence);
882 this->write(")");
883 return true;
884 }
885 case k_textureWrite_IntrinsicKind: {
886 this->writeExpression(*arguments[0], Precedence::kExpression);
887 this->write(".write(");
888 this->writeExpression(*arguments[2], Precedence::kSequence);
889 this->write(", ");
890 this->writeExpression(*arguments[1], Precedence::kSequence);
891 this->write(")");
892 return true;
893 }
894 case k_textureWidth_IntrinsicKind: {
895 this->writeExpression(*arguments[0], Precedence::kExpression);
896 this->write(".get_width()");
897 return true;
898 }
899 case k_textureHeight_IntrinsicKind: {
900 this->writeExpression(*arguments[0], Precedence::kExpression);
901 this->write(".get_height()");
902 return true;
903 }
904 case k_mod_IntrinsicKind: {
905 // fmod(x, y) in metal calculates x - y * trunc(x / y) instead of x - y * floor(x / y)
906 std::string tmpX = this->getTempVariable(arguments[0]->type());
907 std::string tmpY = this->getTempVariable(arguments[1]->type());
908 this->write("(" + tmpX + " = ");
909 this->writeExpression(*arguments[0], Precedence::kSequence);
910 this->write(", " + tmpY + " = ");
911 this->writeExpression(*arguments[1], Precedence::kSequence);
912 this->write(", " + tmpX + " - " + tmpY + " * floor(" + tmpX + " / " + tmpY + "))");
913 return true;
914 }
915 // GLSL declares scalar versions of most geometric intrinsics, but these don't exist in MSL
916 case k_distance_IntrinsicKind: {
917 if (arguments[0]->type().columns() == 1) {
918 this->write("abs(");
919 this->writeExpression(*arguments[0], Precedence::kAdditive);
920 this->write(" - ");
921 this->writeExpression(*arguments[1], Precedence::kAdditive);
922 this->write(")");
923 } else {
924 this->writeSimpleIntrinsic(c);
925 }
926 return true;
927 }
928 case k_dot_IntrinsicKind: {
929 if (arguments[0]->type().columns() == 1) {
930 this->write("(");
931 this->writeExpression(*arguments[0], Precedence::kMultiplicative);
932 this->write(" * ");
933 this->writeExpression(*arguments[1], Precedence::kMultiplicative);
934 this->write(")");
935 } else {
936 this->writeSimpleIntrinsic(c);
937 }
938 return true;
939 }
940 case k_faceforward_IntrinsicKind: {
941 if (arguments[0]->type().columns() == 1) {
942 // ((((Nref) * (I) < 0) ? 1 : -1) * (N))
943 this->write("((((");
944 this->writeExpression(*arguments[2], Precedence::kSequence);
945 this->write(") * (");
946 this->writeExpression(*arguments[1], Precedence::kSequence);
947 this->write(") < 0) ? 1 : -1) * (");
948 this->writeExpression(*arguments[0], Precedence::kSequence);
949 this->write("))");
950 } else {
951 this->writeSimpleIntrinsic(c);
952 }
953 return true;
954 }
955 case k_length_IntrinsicKind: {
956 this->write(arguments[0]->type().columns() == 1 ? "abs(" : "length(");
957 this->writeExpression(*arguments[0], Precedence::kSequence);
958 this->write(")");
959 return true;
960 }
961 case k_normalize_IntrinsicKind: {
962 this->write(arguments[0]->type().columns() == 1 ? "sign(" : "normalize(");
963 this->writeExpression(*arguments[0], Precedence::kSequence);
964 this->write(")");
965 return true;
966 }
967 case k_packUnorm2x16_IntrinsicKind: {
968 this->write("pack_float_to_unorm2x16(");
969 this->writeExpression(*arguments[0], Precedence::kSequence);
970 this->write(")");
971 return true;
972 }
973 case k_unpackUnorm2x16_IntrinsicKind: {
974 this->write("unpack_unorm2x16_to_float(");
975 this->writeExpression(*arguments[0], Precedence::kSequence);
976 this->write(")");
977 return true;
978 }
979 case k_packSnorm2x16_IntrinsicKind: {
980 this->write("pack_float_to_snorm2x16(");
981 this->writeExpression(*arguments[0], Precedence::kSequence);
982 this->write(")");
983 return true;
984 }
985 case k_unpackSnorm2x16_IntrinsicKind: {
986 this->write("unpack_snorm2x16_to_float(");
987 this->writeExpression(*arguments[0], Precedence::kSequence);
988 this->write(")");
989 return true;
990 }
991 case k_packUnorm4x8_IntrinsicKind: {
992 this->write("pack_float_to_unorm4x8(");
993 this->writeExpression(*arguments[0], Precedence::kSequence);
994 this->write(")");
995 return true;
996 }
997 case k_unpackUnorm4x8_IntrinsicKind: {
998 this->write("unpack_unorm4x8_to_float(");
999 this->writeExpression(*arguments[0], Precedence::kSequence);
1000 this->write(")");
1001 return true;
1002 }
1003 case k_packSnorm4x8_IntrinsicKind: {
1004 this->write("pack_float_to_snorm4x8(");
1005 this->writeExpression(*arguments[0], Precedence::kSequence);
1006 this->write(")");
1007 return true;
1008 }
1009 case k_unpackSnorm4x8_IntrinsicKind: {
1010 this->write("unpack_snorm4x8_to_float(");
1011 this->writeExpression(*arguments[0], Precedence::kSequence);
1012 this->write(")");
1013 return true;
1014 }
1015 case k_packHalf2x16_IntrinsicKind: {
1016 this->write("as_type<uint>(half2(");
1017 this->writeExpression(*arguments[0], Precedence::kSequence);
1018 this->write("))");
1019 return true;
1020 }
1021 case k_unpackHalf2x16_IntrinsicKind: {
1022 this->write("float2(as_type<half2>(");
1023 this->writeExpression(*arguments[0], Precedence::kSequence);
1024 this->write("))");
1025 return true;
1026 }
1027 case k_floatBitsToInt_IntrinsicKind:
1028 case k_floatBitsToUint_IntrinsicKind:
1029 case k_intBitsToFloat_IntrinsicKind:
1030 case k_uintBitsToFloat_IntrinsicKind: {
1031 this->write(this->getBitcastIntrinsic(c.type()));
1032 this->write("(");
1033 this->writeExpression(*arguments[0], Precedence::kSequence);
1034 this->write(")");
1035 return true;
1036 }
1037 case k_degrees_IntrinsicKind: {
1038 this->write("((");
1039 this->writeExpression(*arguments[0], Precedence::kSequence);
1040 this->write(") * 57.2957795)");
1041 return true;
1042 }
1043 case k_radians_IntrinsicKind: {
1044 this->write("((");
1045 this->writeExpression(*arguments[0], Precedence::kSequence);
1046 this->write(") * 0.0174532925)");
1047 return true;
1048 }
1049 case k_dFdx_IntrinsicKind: {
1050 this->write("dfdx");
1051 this->writeArgumentList(c.arguments());
1052 return true;
1053 }
1054 case k_dFdy_IntrinsicKind: {
1055 if (!fRTFlipName.empty()) {
1056 this->write("(" + fRTFlipName + ".y * dfdy");
1057 } else {
1058 this->write("(dfdy");
1059 }
1060 this->writeArgumentList(c.arguments());
1061 this->write(")");
1062 return true;
1063 }
1064 case k_inverse_IntrinsicKind: {
1065 this->write(this->getInversePolyfill(arguments));
1066 this->writeArgumentList(c.arguments());
1067 return true;
1068 }
1069 case k_inversesqrt_IntrinsicKind: {
1070 this->write("rsqrt");
1071 this->writeArgumentList(c.arguments());
1072 return true;
1073 }
1074 case k_atan_IntrinsicKind: {
1075 this->write(c.arguments().size() == 2 ? "atan2" : "atan");
1076 this->writeArgumentList(c.arguments());
1077 return true;
1078 }
1079 case k_reflect_IntrinsicKind: {
1080 if (arguments[0]->type().columns() == 1) {
1081 // We need to synthesize `I - 2 * N * I * N`.
1082 std::string tmpI = this->getTempVariable(arguments[0]->type());
1083 std::string tmpN = this->getTempVariable(arguments[1]->type());
1084
1085 // (_skTempI = ...
1086 this->write("(" + tmpI + " = ");
1087 this->writeExpression(*arguments[0], Precedence::kSequence);
1088
1089 // , _skTempN = ...
1090 this->write(", " + tmpN + " = ");
1091 this->writeExpression(*arguments[1], Precedence::kSequence);
1092
1093 // , _skTempI - 2 * _skTempN * _skTempI * _skTempN)
1094 this->write(", " + tmpI + " - 2 * " + tmpN + " * " + tmpI + " * " + tmpN + ")");
1095 } else {
1096 this->writeSimpleIntrinsic(c);
1097 }
1098 return true;
1099 }
1100 case k_refract_IntrinsicKind: {
1101 if (arguments[0]->type().columns() == 1) {
1102 // Metal does implement refract for vectors; rather than reimplementing refract from
1103 // scratch, we can replace the call with `refract(float2(I,0), float2(N,0), eta).x`.
1104 this->write("(refract(float2(");
1105 this->writeExpression(*arguments[0], Precedence::kSequence);
1106 this->write(", 0), float2(");
1107 this->writeExpression(*arguments[1], Precedence::kSequence);
1108 this->write(", 0), ");
1109 this->writeExpression(*arguments[2], Precedence::kSequence);
1110 this->write(").x)");
1111 } else {
1112 this->writeSimpleIntrinsic(c);
1113 }
1114 return true;
1115 }
1116 case k_roundEven_IntrinsicKind: {
1117 this->write("rint");
1118 this->writeArgumentList(c.arguments());
1119 return true;
1120 }
1121 case k_bitCount_IntrinsicKind: {
1122 this->write("popcount(");
1123 this->writeExpression(*arguments[0], Precedence::kSequence);
1124 this->write(")");
1125 return true;
1126 }
1127 case k_findLSB_IntrinsicKind: {
1128 // Create a temp variable to store the expression, to avoid double-evaluating it.
1129 std::string skTemp = this->getTempVariable(arguments[0]->type());
1130 std::string exprType = this->typeName(arguments[0]->type());
1131
1132 // ctz returns numbits(type) on zero inputs; GLSL documents it as generating -1 instead.
1133 // Use select to detect zero inputs and force a -1 result.
1134
1135 // (_skTemp1 = (.....), select(ctz(_skTemp1), int4(-1), _skTemp1 == int4(0)))
1136 this->write("(");
1137 this->write(skTemp);
1138 this->write(" = (");
1139 this->writeExpression(*arguments[0], Precedence::kSequence);
1140 this->write("), select(ctz(");
1141 this->write(skTemp);
1142 this->write("), ");
1143 this->write(exprType);
1144 this->write("(-1), ");
1145 this->write(skTemp);
1146 this->write(" == ");
1147 this->write(exprType);
1148 this->write("(0)))");
1149 return true;
1150 }
1151 case k_findMSB_IntrinsicKind: {
1152 // Create a temp variable to store the expression, to avoid double-evaluating it.
1153 std::string skTemp1 = this->getTempVariable(arguments[0]->type());
1154 std::string exprType = this->typeName(arguments[0]->type());
1155
1156 // GLSL findMSB is actually quite different from Metal's clz:
1157 // - For signed negative numbers, it returns the first zero bit, not the first one bit!
1158 // - For an empty input (0/~0 depending on sign), findMSB gives -1; clz is numbits(type)
1159
1160 // (_skTemp1 = (.....),
1161 this->write("(");
1162 this->write(skTemp1);
1163 this->write(" = (");
1164 this->writeExpression(*arguments[0], Precedence::kSequence);
1165 this->write("), ");
1166
1167 // Signed input types might be negative; we need another helper variable to negate the
1168 // input (since we can only find one bits, not zero bits).
1169 std::string skTemp2;
1170 if (arguments[0]->type().isSigned()) {
1171 // ... _skTemp2 = (select(_skTemp1, ~_skTemp1, _skTemp1 < 0)),
1172 skTemp2 = this->getTempVariable(arguments[0]->type());
1173 this->write(skTemp2);
1174 this->write(" = (select(");
1175 this->write(skTemp1);
1176 this->write(", ~");
1177 this->write(skTemp1);
1178 this->write(", ");
1179 this->write(skTemp1);
1180 this->write(" < 0)), ");
1181 } else {
1182 skTemp2 = skTemp1;
1183 }
1184
1185 // ... select(int4(clz(_skTemp2)), int4(-1), _skTemp2 == int4(0)))
1186 this->write("select(");
1187 this->write(this->typeName(c.type()));
1188 this->write("(clz(");
1189 this->write(skTemp2);
1190 this->write(")), ");
1191 this->write(this->typeName(c.type()));
1192 this->write("(-1), ");
1193 this->write(skTemp2);
1194 this->write(" == ");
1195 this->write(exprType);
1196 this->write("(0)))");
1197 return true;
1198 }
1199 case k_sign_IntrinsicKind: {
1200 if (arguments[0]->type().componentType().isInteger()) {
1201 // Create a temp variable to store the expression, to avoid double-evaluating it.
1202 std::string skTemp = this->getTempVariable(arguments[0]->type());
1203 std::string exprType = this->typeName(arguments[0]->type());
1204
1205 // (_skTemp = (.....),
1206 this->write("(");
1207 this->write(skTemp);
1208 this->write(" = (");
1209 this->writeExpression(*arguments[0], Precedence::kSequence);
1210 this->write("), ");
1211
1212 // ... select(select(int4(0), int4(-1), _skTemp < 0), int4(1), _skTemp > 0))
1213 this->write("select(select(");
1214 this->write(exprType);
1215 this->write("(0), ");
1216 this->write(exprType);
1217 this->write("(-1), ");
1218 this->write(skTemp);
1219 this->write(" < 0), ");
1220 this->write(exprType);
1221 this->write("(1), ");
1222 this->write(skTemp);
1223 this->write(" > 0))");
1224 } else {
1225 this->writeSimpleIntrinsic(c);
1226 }
1227 return true;
1228 }
1229 case k_matrixCompMult_IntrinsicKind: {
1230 this->writeMatrixCompMult();
1231 this->writeSimpleIntrinsic(c);
1232 return true;
1233 }
1234 case k_outerProduct_IntrinsicKind: {
1235 this->writeOuterProduct();
1236 this->writeSimpleIntrinsic(c);
1237 return true;
1238 }
1239 case k_mix_IntrinsicKind: {
1240 SkASSERT(c.arguments().size() == 3);
1241 if (arguments[2]->type().componentType().isBoolean()) {
1242 // The Boolean forms of GLSL mix() use the select() intrinsic in Metal.
1243 this->write("select");
1244 this->writeArgumentList(c.arguments());
1245 return true;
1246 }
1247 // The basic form of mix() is supported by Metal as-is.
1248 this->writeSimpleIntrinsic(c);
1249 return true;
1250 }
1251 case k_equal_IntrinsicKind:
1252 case k_greaterThan_IntrinsicKind:
1253 case k_greaterThanEqual_IntrinsicKind:
1254 case k_lessThan_IntrinsicKind:
1255 case k_lessThanEqual_IntrinsicKind:
1256 case k_notEqual_IntrinsicKind: {
1257 this->write("(");
1258 this->writeExpression(*c.arguments()[0], Precedence::kRelational);
1259 switch (kind) {
1260 case k_equal_IntrinsicKind:
1261 this->write(" == ");
1262 break;
1263 case k_notEqual_IntrinsicKind:
1264 this->write(" != ");
1265 break;
1266 case k_lessThan_IntrinsicKind:
1267 this->write(" < ");
1268 break;
1269 case k_lessThanEqual_IntrinsicKind:
1270 this->write(" <= ");
1271 break;
1272 case k_greaterThan_IntrinsicKind:
1273 this->write(" > ");
1274 break;
1275 case k_greaterThanEqual_IntrinsicKind:
1276 this->write(" >= ");
1277 break;
1278 default:
1279 SK_ABORT("unsupported comparison intrinsic kind");
1280 }
1281 this->writeExpression(*c.arguments()[1], Precedence::kRelational);
1282 this->write(")");
1283 return true;
1284 }
1285 case k_storageBarrier_IntrinsicKind:
1286 this->write("threadgroup_barrier(mem_flags::mem_device)");
1287 return true;
1288 case k_workgroupBarrier_IntrinsicKind:
1289 this->write("threadgroup_barrier(mem_flags::mem_threadgroup)");
1290 return true;
1291 case k_atomicAdd_IntrinsicKind:
1292 this->write("atomic_fetch_add_explicit(&");
1293 this->writeExpression(*c.arguments()[0], Precedence::kSequence);
1294 this->write(", ");
1295 this->writeExpression(*c.arguments()[1], Precedence::kSequence);
1296 this->write(", memory_order_relaxed)");
1297 return true;
1298 case k_atomicLoad_IntrinsicKind:
1299 this->write("atomic_load_explicit(&");
1300 this->writeExpression(*c.arguments()[0], Precedence::kSequence);
1301 this->write(", memory_order_relaxed)");
1302 return true;
1303 case k_atomicStore_IntrinsicKind:
1304 this->write("atomic_store_explicit(&");
1305 this->writeExpression(*c.arguments()[0], Precedence::kSequence);
1306 this->write(", ");
1307 this->writeExpression(*c.arguments()[1], Precedence::kSequence);
1308 this->write(", memory_order_relaxed)");
1309 return true;
1310 default:
1311 return false;
1312 }
1313}
#define SK_ABORT(message,...)
Definition SkAssert.h:70
void writeArgumentList(const ExpressionArray &arguments)
std::string getInversePolyfill(const ExpressionArray &arguments)
void writeSimpleIntrinsic(const FunctionCall &c)
std::string getBitcastIntrinsic(const Type &outType)

◆ writeLayout()

void SkSL::MetalCodeGenerator::writeLayout ( const Layout layout)
protected

◆ writeLine()

void SkSL::MetalCodeGenerator::writeLine ( std::string_view  s = std::string_view())
protected

Definition at line 406 of file SkSLMetalCodeGenerator.cpp.

406 {
407 this->write(s);
409 fAtLineStart = true;
410}

◆ writeLiteral()

void SkSL::MetalCodeGenerator::writeLiteral ( const Literal f)
protected

Definition at line 2265 of file SkSLMetalCodeGenerator.cpp.

2265 {
2266 const Type& type = l.type();
2267 if (type.isFloat()) {
2268 this->write(l.description(OperatorPrecedence::kExpression));
2269 if (!l.type().highPrecision()) {
2270 this->write("h");
2271 }
2272 return;
2273 }
2274 if (type.isInteger()) {
2275 if (type.matches(*fContext.fTypes.fUInt)) {
2276 this->write(std::to_string(l.intValue() & 0xffffffff));
2277 this->write("u");
2278 } else if (type.matches(*fContext.fTypes.fUShort)) {
2279 this->write(std::to_string(l.intValue() & 0xffff));
2280 this->write("u");
2281 } else {
2282 this->write(std::to_string(l.intValue()));
2283 }
2284 return;
2285 }
2286 SkASSERT(type.isBoolean());
2287 this->write(l.description(OperatorPrecedence::kExpression));
2288}
const std::unique_ptr< Type > fUShort
const std::unique_ptr< Type > fUInt

◆ writeMatrixCompMult()

void SkSL::MetalCodeGenerator::writeMatrixCompMult ( )
protected

Definition at line 823 of file SkSLMetalCodeGenerator.cpp.

823 {
824 static constexpr char kMatrixCompMult[] = R"(
825template <typename T, int C, int R>
826matrix<T, C, R> matrixCompMult(matrix<T, C, R> a, const matrix<T, C, R> b) {
827 for (int c = 0; c < C; ++c) { a[c] *= b[c]; }
828 return a;
829}
830)";
833 fExtraFunctions.writeText(kMatrixCompMult);
834 }
835}

◆ writeMatrixDivisionHelpers()

void SkSL::MetalCodeGenerator::writeMatrixDivisionHelpers ( const Type type)
protected

Definition at line 1956 of file SkSLMetalCodeGenerator.cpp.

1956 {
1957 SkASSERT(type.isMatrix());
1958
1959 std::string key = "Matrix / " + this->typeName(type);
1960
1961 if (!fHelpers.contains(key)) {
1962 fHelpers.add(key);
1963 std::string typeName = this->typeName(type);
1964
1966 "thread %s operator/(const %s left, const %s right) {\n"
1967 " return %s(",
1968 typeName.c_str(), typeName.c_str(), typeName.c_str(), typeName.c_str());
1969
1970 const char* separator = "";
1971 for (int index=0; index<type.columns(); ++index) {
1972 fExtraFunctions.printf("%sleft[%d] / right[%d]", separator, index, index);
1973 separator = ", ";
1974 }
1975
1976 fExtraFunctions.printf(");\n"
1977 "}\n"
1978 "thread %s& operator/=(thread %s& left, thread const %s& right) {\n"
1979 " left = left / right;\n"
1980 " return left;\n"
1981 "}\n",
1982 typeName.c_str(), typeName.c_str(), typeName.c_str());
1983 }
1984}

◆ writeMatrixEqualityHelpers()

void SkSL::MetalCodeGenerator::writeMatrixEqualityHelpers ( const Type left,
const Type right 
)
protected

Definition at line 1916 of file SkSLMetalCodeGenerator.cpp.

1916 {
1917 SkASSERT(left.isMatrix());
1918 SkASSERT(right.isMatrix());
1919 SkASSERT(left.rows() == right.rows());
1920 SkASSERT(left.columns() == right.columns());
1921
1922 std::string key = "Matrix == " + this->typeName(left) + ":" + this->typeName(right);
1923
1924 if (!fHelpers.contains(key)) {
1925 fHelpers.add(key);
1927thread bool operator==(const %s left, const %s right);
1928thread bool operator!=(const %s left, const %s right);
1929)",
1930 this->typeName(left).c_str(),
1931 this->typeName(right).c_str(),
1932 this->typeName(left).c_str(),
1933 this->typeName(right).c_str());
1934
1936 "thread bool operator==(const %s left, const %s right) {\n"
1937 " return ",
1938 this->typeName(left).c_str(), this->typeName(right).c_str());
1939
1940 const char* separator = "";
1941 for (int index=0; index<left.columns(); ++index) {
1942 fExtraFunctions.printf("%sall(left[%d] == right[%d])", separator, index, index);
1943 separator = " &&\n ";
1944 }
1945
1947 ";\n"
1948 "}\n"
1949 "thread bool operator!=(const %s left, const %s right) {\n"
1950 " return !(left == right);\n"
1951 "}\n",
1952 this->typeName(left).c_str(), this->typeName(right).c_str());
1953 }
1954}

◆ writeMatrixTimesEqualHelper()

void SkSL::MetalCodeGenerator::writeMatrixTimesEqualHelper ( const Type left,
const Type right,
const Type result 
)
protected

Definition at line 1897 of file SkSLMetalCodeGenerator.cpp.

1898 {
1899 SkASSERT(left.isMatrix());
1900 SkASSERT(right.isMatrix());
1901 SkASSERT(result.isMatrix());
1902
1903 std::string key = "Matrix *= " + this->typeName(left) + ":" + this->typeName(right);
1904
1905 if (!fHelpers.contains(key)) {
1906 fHelpers.add(key);
1907 fExtraFunctions.printf("thread %s& operator*=(thread %s& left, thread const %s& right) {\n"
1908 " left = left * right;\n"
1909 " return left;\n"
1910 "}\n",
1911 this->typeName(result).c_str(), this->typeName(left).c_str(),
1912 this->typeName(right).c_str());
1913 }
1914}
GAsyncResult * result

◆ writeMinAbsHack()

void SkSL::MetalCodeGenerator::writeMinAbsHack ( Expression absExpr,
Expression otherExpr 
)
protected

◆ writeModifiers()

void SkSL::MetalCodeGenerator::writeModifiers ( ModifierFlags  flags)
protected

Definition at line 2664 of file SkSLMetalCodeGenerator.cpp.

2664 {
2667 this->write("device ");
2668 } else if (flags & ModifierFlag::kOut) {
2669 this->write("thread ");
2670 }
2671 if (flags.isConst()) {
2672 this->write("const ");
2673 }
2674}
FlutterSemanticsFlag flags

◆ writeName()

void SkSL::MetalCodeGenerator::writeName ( std::string_view  name)
protected

Definition at line 2774 of file SkSLMetalCodeGenerator.cpp.

2774 {
2776 this->write("_"); // adding underscore before name to avoid conflict with reserved words
2777 }
2778 this->write(name);
2779}

◆ writeNumberAsMatrix()

void SkSL::MetalCodeGenerator::writeNumberAsMatrix ( const Expression expr,
const Type matrixType 
)
protected

Definition at line 2093 of file SkSLMetalCodeGenerator.cpp.

2093 {
2094 SkASSERT(expr.type().isNumber());
2095 SkASSERT(matrixType.isMatrix());
2096
2097 // Componentwise multiply the scalar against a matrix of the desired size which contains all 1s.
2098 this->write("(");
2099 this->writeType(matrixType);
2100 this->write("(");
2101
2102 const char* separator = "";
2103 for (int index = matrixType.slotCount(); index--;) {
2104 this->write(separator);
2105 this->write("1.0");
2106 separator = ", ";
2107 }
2108
2109 this->write(") * ");
2110 this->writeExpression(expr, Precedence::kMultiplicative);
2111 this->write(")");
2112}

◆ writeOuterProduct()

void SkSL::MetalCodeGenerator::writeOuterProduct ( )
protected

Definition at line 837 of file SkSLMetalCodeGenerator.cpp.

837 {
838 static constexpr char kOuterProduct[] = R"(
839template <typename T, int C, int R>
840matrix<T, C, R> outerProduct(const vec<T, R> a, const vec<T, C> b) {
841 matrix<T, C, R> m;
842 for (int c = 0; c < C; ++c) { m[c] = a * b[c]; }
843 return m;
844}
845)";
848 fExtraFunctions.writeText(kOuterProduct);
849 }
850}

◆ writeOutputStruct()

void SkSL::MetalCodeGenerator::writeOutputStruct ( )
protected

Definition at line 3090 of file SkSLMetalCodeGenerator.cpp.

3090 {
3091 this->write("struct Outputs {\n");
3093 this->write(" float4 sk_Position [[position]];\n");
3094 } else if (ProgramConfig::IsFragment(fProgram.fConfig->fKind)) {
3095 this->write(" half4 sk_FragColor [[color(0)]];\n");
3097 this->write(" half4 sk_SecondaryFragColor [[color(0), index(1)]];\n");
3098 }
3099 }
3100 for (const ProgramElement* e : fProgram.elements()) {
3101 if (e->is<GlobalVarDeclaration>()) {
3102 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
3103 const Variable& var = *decls.varDeclaration().var();
3104 if (var.layout().fBuiltin == SK_SAMPLEMASK_BUILTIN) {
3105 this->write(" uint sk_SampleMask [[sample_mask]];\n");
3106 continue;
3107 }
3108 if (is_output(var)) {
3109 this->write(" ");
3111 needs_address_space(var.type(), var.modifierFlags())) {
3112 // TODO: address space support
3113 this->write("device ");
3114 }
3115 this->writeType(var.type());
3117 pass_by_reference(var.type(), var.modifierFlags())) {
3118 this->write("&");
3119 }
3120 this->write(" ");
3121 this->writeName(var.mangledName());
3122
3123 int location = var.layout().fLocation;
3124 if (!ProgramConfig::IsCompute(fProgram.fConfig->fKind) && location < 0 &&
3125 var.type().typeKind() != Type::TypeKind::kTexture) {
3126 fContext.fErrors->error(var.fPosition,
3127 "Metal out variables must have 'layout(location=...)'");
3128 } else if (ProgramConfig::IsVertex(fProgram.fConfig->fKind)) {
3129 this->write(" [[user(locn" + std::to_string(location) + ")]]");
3130 } else if (ProgramConfig::IsFragment(fProgram.fConfig->fKind)) {
3131 this->write(" [[color(" + std::to_string(location) + ")");
3132 int colorIndex = var.layout().fIndex;
3133 if (colorIndex) {
3134 this->write(", index(" + std::to_string(colorIndex) + ")");
3135 }
3136 this->write("]]");
3137 }
3138 this->write(";\n");
3139 }
3140 }
3141 }
3143 this->write(" float sk_PointSize [[point_size]];\n");
3144 }
3145 this->write("};\n");
3146}

◆ writePostfixExpression()

void SkSL::MetalCodeGenerator::writePostfixExpression ( const PostfixExpression p,
Precedence  parentPrecedence 
)
protected

Definition at line 2253 of file SkSLMetalCodeGenerator.cpp.

2254 {
2255 if (Precedence::kPostfix >= parentPrecedence) {
2256 this->write("(");
2257 }
2258 this->writeExpression(*p.operand(), Precedence::kPostfix);
2259 this->write(p.getOperator().tightOperatorName());
2260 if (Precedence::kPostfix >= parentPrecedence) {
2261 this->write(")");
2262 }
2263}

◆ writePrecisionModifier()

void SkSL::MetalCodeGenerator::writePrecisionModifier ( )
protected

◆ writePrefixExpression()

void SkSL::MetalCodeGenerator::writePrefixExpression ( const PrefixExpression p,
Precedence  parentPrecedence 
)
protected

Definition at line 2222 of file SkSLMetalCodeGenerator.cpp.

2223 {
2224 // According to the MSL specification, the arithmetic unary operators (+ and –) do not act
2225 // upon matrix type operands. We treat the unary "+" as a no-op for all operands.
2226 const Operator op = p.getOperator();
2227 if (op.kind() == Operator::Kind::PLUS) {
2228 this->writeExpression(*p.operand(), Precedence::kPrefix);
2229 return;
2230 }
2231
2232 if (op.kind() == Operator::Kind::MINUS && p.operand()->type().isMatrix()) {
2233 // Transform the unary "-" on a matrix type to a multiplication by -1.
2234 this->write(p.type().componentType().highPrecision() ? "(-1.0 * "
2235 : "(-1.0h * ");
2236 this->writeExpression(*p.operand(), Precedence::kMultiplicative);
2237 this->write(")");
2238 return;
2239 }
2240
2241 if (Precedence::kPrefix >= parentPrecedence) {
2242 this->write("(");
2243 }
2244
2245 this->write(p.getOperator().tightOperatorName());
2246 this->writeExpression(*p.operand(), Precedence::kPrefix);
2247
2248 if (Precedence::kPrefix >= parentPrecedence) {
2249 this->write(")");
2250 }
2251}

◆ writeProgramElement()

void SkSL::MetalCodeGenerator::writeProgramElement ( const ProgramElement e)
protected

Definition at line 3425 of file SkSLMetalCodeGenerator.cpp.

3425 {
3426 switch (e.kind()) {
3427 case ProgramElement::Kind::kExtension:
3428 break;
3429 case ProgramElement::Kind::kGlobalVar:
3430 break;
3431 case ProgramElement::Kind::kInterfaceBlock:
3432 // Handled in writeInterfaceBlocks; do nothing.
3433 break;
3434 case ProgramElement::Kind::kStructDefinition:
3435 // Handled in writeStructDefinitions; do nothing.
3436 break;
3437 case ProgramElement::Kind::kFunction:
3438 this->writeFunction(e.as<FunctionDefinition>());
3439 break;
3440 case ProgramElement::Kind::kFunctionPrototype:
3441 this->writeFunctionPrototype(e.as<FunctionPrototype>());
3442 break;
3443 case ProgramElement::Kind::kModifiers:
3444 // Not necessary in Metal; do nothing.
3445 break;
3446 default:
3447 SkDEBUGFAILF("unsupported program element: %s\n", e.description().c_str());
3448 break;
3449 }
3450}
void writeFunction(const FunctionDefinition &f)
void writeFunctionPrototype(const FunctionPrototype &f)

◆ writeReturnStatement()

void SkSL::MetalCodeGenerator::writeReturnStatement ( const ReturnStatement r)
protected

Definition at line 2949 of file SkSLMetalCodeGenerator.cpp.

2949 {
2951 if (r.expression()) {
2952 if (r.expression()->type().matches(*fContext.fTypes.fHalf4)) {
2953 this->write("_out.sk_FragColor = ");
2954 this->writeExpression(*r.expression(), Precedence::kExpression);
2955 this->writeLine(";");
2956 } else {
2957 fContext.fErrors->error(r.fPosition,
2958 "Metal does not support returning '" +
2959 r.expression()->type().description() + "' from main()");
2960 }
2961 }
2963 return;
2964 }
2965
2966 this->write("return");
2967 if (r.expression()) {
2968 this->write(" ");
2969 this->writeExpression(*r.expression(), Precedence::kExpression);
2970 }
2971 this->write(";");
2972}
const std::unique_ptr< Type > fHalf4

◆ writeReturnStatementFromMain()

void SkSL::MetalCodeGenerator::writeReturnStatementFromMain ( )
protected

Definition at line 2937 of file SkSLMetalCodeGenerator.cpp.

2937 {
2938 // main functions in Metal return a magic _out parameter that doesn't exist in SkSL.
2941 this->write("return _out;");
2942 } else if (ProgramConfig::IsCompute(fProgram.fConfig->fKind)) {
2943 this->write("return;");
2944 } else {
2945 SkDEBUGFAIL("unsupported kind of program");
2946 }
2947}

◆ writeSampler2DPolyfill()

void SkSL::MetalCodeGenerator::writeSampler2DPolyfill ( )
protected

Definition at line 2983 of file SkSLMetalCodeGenerator.cpp.

2983 {
2984 class : public GlobalStructVisitor {
2985 public:
2986 void visitSampler(const Type&, std::string_view) override {
2987 if (fWrotePolyfill) {
2988 return;
2989 }
2990 fWrotePolyfill = true;
2991
2992 std::string polyfill = SkSL::String::printf(R"(
2993struct sampler2D {
2994 texture2d<half> tex;
2995 sampler smp;
2996};
2997half4 sample(sampler2D i, float2 p, float b=%g) { return i.tex.sample(i.smp, p, bias(b)); }
2998half4 sample(sampler2D i, float3 p, float b=%g) { return i.tex.sample(i.smp, p.xy / p.z, bias(b)); }
2999half4 sampleLod(sampler2D i, float2 p, float lod) { return i.tex.sample(i.smp, p, level(lod)); }
3000half4 sampleLod(sampler2D i, float3 p, float lod) {
3001 return i.tex.sample(i.smp, p.xy / p.z, level(lod));
3002}
3003half4 sampleGrad(sampler2D i, float2 p, float2 dPdx, float2 dPdy) {
3004 return i.tex.sample(i.smp, p, gradient2d(dPdx, dPdy));
3005}
3006
3007)",
3008 fTextureBias,
3009 fTextureBias);
3010 fCodeGen->write(polyfill.c_str());
3011 }
3012
3013 MetalCodeGenerator* fCodeGen = nullptr;
3014 float fTextureBias = 0.0f;
3015 bool fWrotePolyfill = false;
3016 } visitor;
3017
3018 visitor.fCodeGen = this;
3019 visitor.fTextureBias = fProgram.fConfig->fSettings.fSharpenTextures ? kSharpenTexturesBias
3020 : 0.0f;
3021 this->visitGlobalStruct(&visitor);
3022}
static constexpr float kSharpenTexturesBias

◆ writeSimpleIntrinsic()

void SkSL::MetalCodeGenerator::writeSimpleIntrinsic ( const FunctionCall c)
protected

Definition at line 858 of file SkSLMetalCodeGenerator.cpp.

858 {
859 // Write out an intrinsic function call exactly as-is. No muss no fuss.
860 this->write(c.function().name());
861 this->writeArgumentList(c.arguments());
862}

◆ writeStatement()

void SkSL::MetalCodeGenerator::writeStatement ( const Statement s)
protected

Definition at line 2793 of file SkSLMetalCodeGenerator.cpp.

2793 {
2794 switch (s.kind()) {
2795 case Statement::Kind::kBlock:
2796 this->writeBlock(s.as<Block>());
2797 break;
2798 case Statement::Kind::kExpression:
2799 this->writeExpressionStatement(s.as<ExpressionStatement>());
2800 break;
2801 case Statement::Kind::kReturn:
2802 this->writeReturnStatement(s.as<ReturnStatement>());
2803 break;
2804 case Statement::Kind::kVarDeclaration:
2805 this->writeVarDeclaration(s.as<VarDeclaration>());
2806 break;
2807 case Statement::Kind::kIf:
2808 this->writeIfStatement(s.as<IfStatement>());
2809 break;
2810 case Statement::Kind::kFor:
2811 this->writeForStatement(s.as<ForStatement>());
2812 break;
2813 case Statement::Kind::kDo:
2814 this->writeDoStatement(s.as<DoStatement>());
2815 break;
2816 case Statement::Kind::kSwitch:
2817 this->writeSwitchStatement(s.as<SwitchStatement>());
2818 break;
2819 case Statement::Kind::kBreak:
2820 this->write("break;");
2821 break;
2822 case Statement::Kind::kContinue:
2823 this->write("continue;");
2824 break;
2825 case Statement::Kind::kDiscard:
2826 this->write("discard_fragment();");
2827 break;
2828 case Statement::Kind::kNop:
2829 this->write(";");
2830 break;
2831 default:
2832 SkDEBUGFAILF("unsupported statement: %s", s.description().c_str());
2833 break;
2834 }
2835}
void writeExpressionStatement(const ExpressionStatement &s)
void writeSwitchStatement(const SwitchStatement &s)
void writeReturnStatement(const ReturnStatement &r)
void writeIfStatement(const IfStatement &stmt)
void writeDoStatement(const DoStatement &d)
void writeVarDeclaration(const VarDeclaration &decl)
void writeForStatement(const ForStatement &f)

◆ writeStatements()

void SkSL::MetalCodeGenerator::writeStatements ( const StatementArray statements)
protected

◆ writeStructDefinition()

void SkSL::MetalCodeGenerator::writeStructDefinition ( const StructDefinition s)
protected

Definition at line 465 of file SkSLMetalCodeGenerator.cpp.

465 {
466 const Type& type = s.type();
467 this->writeLine("struct " + type.displayName() + " {");
468 fIndentation++;
469 this->writeFields(type.fields(), type.fPosition);
470 fIndentation--;
471 this->writeLine("};");
472}

◆ writeStructDefinitions()

void SkSL::MetalCodeGenerator::writeStructDefinitions ( )
protected

Definition at line 3164 of file SkSLMetalCodeGenerator.cpp.

3164 {
3165 for (const ProgramElement* e : fProgram.elements()) {
3166 if (e->is<StructDefinition>()) {
3167 this->writeStructDefinition(e->as<StructDefinition>());
3168 }
3169 }
3170}
void writeStructDefinition(const StructDefinition &s)

◆ writeStructEqualityHelpers()

void SkSL::MetalCodeGenerator::writeStructEqualityHelpers ( const Type type)
protected

Definition at line 2023 of file SkSLMetalCodeGenerator.cpp.

2023 {
2024 SkASSERT(type.isStruct());
2025 std::string key = "StructEquality " + this->typeName(type);
2026
2027 if (!fHelpers.contains(key)) {
2028 fHelpers.add(key);
2029 // If one of the struct's fields needs a helper as well, we need to emit that one first.
2030 for (const Field& field : type.fields()) {
2031 this->writeEqualityHelpers(*field.fType, *field.fType);
2032 }
2033
2034 // Write operator== and operator!= for this struct, since those are assumed to exist in SkSL
2035 // and GLSL but do not exist by default in Metal.
2037thread bool operator==(thread const %s& left, thread const %s& right);
2038thread bool operator!=(thread const %s& left, thread const %s& right);
2039)",
2040 this->typeName(type).c_str(),
2041 this->typeName(type).c_str(),
2042 this->typeName(type).c_str(),
2043 this->typeName(type).c_str());
2044
2046 "thread bool operator==(thread const %s& left, thread const %s& right) {\n"
2047 " return ",
2048 this->typeName(type).c_str(),
2049 this->typeName(type).c_str());
2050
2051 const char* separator = "";
2052 for (const Field& field : type.fields()) {
2053 if (field.fType->isArray()) {
2055 "%s(make_array_ref(left.%.*s) == make_array_ref(right.%.*s))",
2056 separator,
2057 (int)field.fName.size(), field.fName.data(),
2058 (int)field.fName.size(), field.fName.data());
2059 } else {
2060 fExtraFunctions.printf("%sall(left.%.*s == right.%.*s)",
2061 separator,
2062 (int)field.fName.size(), field.fName.data(),
2063 (int)field.fName.size(), field.fName.data());
2064 }
2065 separator = " &&\n ";
2066 }
2068 ";\n"
2069 "}\n"
2070 "thread bool operator!=(thread const %s& left, thread const %s& right) {\n"
2071 " return !(left == right);\n"
2072 "}\n",
2073 this->typeName(type).c_str(),
2074 this->typeName(type).c_str());
2075 }
2076}

◆ writeSwitchStatement()

void SkSL::MetalCodeGenerator::writeSwitchStatement ( const SwitchStatement s)
protected

Definition at line 2912 of file SkSLMetalCodeGenerator.cpp.

2912 {
2913 this->write("switch (");
2914 this->writeExpression(*s.value(), Precedence::kExpression);
2915 this->writeLine(") {");
2916 fIndentation++;
2917 for (const std::unique_ptr<Statement>& stmt : s.cases()) {
2918 const SwitchCase& c = stmt->as<SwitchCase>();
2919 if (c.isDefault()) {
2920 this->writeLine("default:");
2921 } else {
2922 this->write("case ");
2923 this->write(std::to_string(c.value()));
2924 this->writeLine(":");
2925 }
2926 if (!c.statement()->isEmpty()) {
2927 fIndentation++;
2928 this->writeStatement(*c.statement());
2929 this->finishLine();
2930 fIndentation--;
2931 }
2932 }
2933 fIndentation--;
2934 this->write("}");
2935}

◆ writeSwizzle()

void SkSL::MetalCodeGenerator::writeSwizzle ( const Swizzle swizzle)
protected

Definition at line 1891 of file SkSLMetalCodeGenerator.cpp.

1891 {
1892 this->writeExpression(*swizzle.base(), Precedence::kPostfix);
1893 this->write(".");
1894 this->write(Swizzle::MaskString(swizzle.components()));
1895}
static std::string MaskString(const ComponentArray &inComponents)

◆ writeTernaryExpression()

void SkSL::MetalCodeGenerator::writeTernaryExpression ( const TernaryExpression t,
Precedence  parentPrecedence 
)
protected

Definition at line 2207 of file SkSLMetalCodeGenerator.cpp.

2208 {
2209 if (Precedence::kTernary >= parentPrecedence) {
2210 this->write("(");
2211 }
2212 this->writeExpression(*t.test(), Precedence::kTernary);
2213 this->write(" ? ");
2214 this->writeExpression(*t.ifTrue(), Precedence::kTernary);
2215 this->write(" : ");
2216 this->writeExpression(*t.ifFalse(), Precedence::kTernary);
2217 if (Precedence::kTernary >= parentPrecedence) {
2218 this->write(")");
2219 }
2220}

◆ writeThreadgroupInit()

void SkSL::MetalCodeGenerator::writeThreadgroupInit ( )
protected

Definition at line 3395 of file SkSLMetalCodeGenerator.cpp.

3395 {
3396 class : public ThreadgroupStructVisitor {
3397 public:
3398 void visitNonconstantVariable(const Variable& var) override {
3399 this->addElement();
3400 fCodeGen->write("{}");
3401 }
3402 void addElement() {
3403 if (fFirst) {
3404 fCodeGen->write("threadgroup Threadgroups _threadgroups{");
3405 fFirst = false;
3406 } else {
3407 fCodeGen->write(", ");
3408 }
3409 }
3410 void finish() {
3411 if (!fFirst) {
3412 fCodeGen->writeLine("};");
3413 fCodeGen->writeLine("(void)_threadgroups;");
3414 }
3415 }
3416 MetalCodeGenerator* fCodeGen = nullptr;
3417 bool fFirst = true;
3418 } visitor;
3419
3420 visitor.fCodeGen = this;
3421 this->visitThreadgroupStruct(&visitor);
3422 visitor.finish();
3423}
void visitThreadgroupStruct(ThreadgroupStructVisitor *visitor)

◆ writeThreadgroupStruct()

void SkSL::MetalCodeGenerator::writeThreadgroupStruct ( )
protected

Definition at line 3361 of file SkSLMetalCodeGenerator.cpp.

3361 {
3362 class : public ThreadgroupStructVisitor {
3363 public:
3364 void visitNonconstantVariable(const Variable& var) override {
3365 this->addElement();
3366 fCodeGen->write(" ");
3367 fCodeGen->writeModifiers(var.modifierFlags());
3368 fCodeGen->writeType(var.type());
3369 fCodeGen->write(" ");
3370 fCodeGen->writeName(var.mangledName());
3371 fCodeGen->write(";\n");
3372 }
3373 void addElement() {
3374 if (fFirst) {
3375 fCodeGen->write("struct Threadgroups {\n");
3376 fFirst = false;
3377 }
3378 }
3379 void finish() {
3380 if (!fFirst) {
3381 fCodeGen->writeLine("};");
3382 fFirst = true;
3383 }
3384 }
3385
3386 MetalCodeGenerator* fCodeGen = nullptr;
3387 bool fFirst = true;
3388 } visitor;
3389
3390 visitor.fCodeGen = this;
3391 this->visitThreadgroupStruct(&visitor);
3392 visitor.finish();
3393}

◆ writeType()

void SkSL::MetalCodeGenerator::writeType ( const Type type)
protected

Definition at line 474 of file SkSLMetalCodeGenerator.cpp.

474 {
475 this->write(this->typeName(type));
476}

◆ writeUniformStruct()

void SkSL::MetalCodeGenerator::writeUniformStruct ( )
protected

Definition at line 3024 of file SkSLMetalCodeGenerator.cpp.

3024 {
3025 for (const ProgramElement* e : fProgram.elements()) {
3026 if (e->is<GlobalVarDeclaration>()) {
3027 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
3028 const Variable& var = *decls.varDeclaration().var();
3029 if (var.modifierFlags().isUniform()) {
3030 SkASSERT(var.type().typeKind() != Type::TypeKind::kSampler &&
3031 var.type().typeKind() != Type::TypeKind::kTexture);
3032 int uniformSet = this->getUniformSet(var.layout());
3033 // Make sure that the program's uniform-set value is consistent throughout.
3034 if (-1 == fUniformBuffer) {
3035 this->write("struct Uniforms {\n");
3036 fUniformBuffer = uniformSet;
3037 } else if (uniformSet != fUniformBuffer) {
3038 fContext.fErrors->error(decls.fPosition,
3039 "Metal backend requires all uniforms to have the same "
3040 "'layout(set=...)'");
3041 }
3042 this->write(" ");
3043 this->writeType(var.type());
3044 this->write(" ");
3045 this->writeName(var.mangledName());
3046 this->write(";\n");
3047 }
3048 }
3049 }
3050 if (-1 != fUniformBuffer) {
3051 this->write("};\n");
3052 }
3053}
int getUniformSet(const Layout &layout)

◆ writeVarDeclaration()

void SkSL::MetalCodeGenerator::writeVarDeclaration ( const VarDeclaration decl)
protected

Definition at line 2781 of file SkSLMetalCodeGenerator.cpp.

2781 {
2782 this->writeModifiers(varDecl.var()->modifierFlags());
2783 this->writeType(varDecl.var()->type());
2784 this->write(" ");
2785 this->writeName(varDecl.var()->mangledName());
2786 if (varDecl.value()) {
2787 this->write(" = ");
2788 this->writeVarInitializer(*varDecl.var(), *varDecl.value());
2789 }
2790 this->write(";");
2791}
void writeVarInitializer(const Variable &var, const Expression &value)

◆ writeVariableReference()

void SkSL::MetalCodeGenerator::writeVariableReference ( const VariableReference ref)
protected

Definition at line 1750 of file SkSLMetalCodeGenerator.cpp.

1750 {
1751 switch (ref.variable()->layout().fBuiltin) {
1753 this->write("_out.sk_FragColor");
1754 break;
1756 this->write("_out.sk_SampleMask");
1757 break;
1760 this->write("_out.sk_SecondaryFragColor");
1761 } else {
1762 fContext.fErrors->error(ref.position(), "'sk_SecondaryFragColor' not supported");
1763 }
1764 break;
1766 this->writeFragCoord();
1767 break;
1769 this->write("sk_SampleMaskIn");
1770 break;
1772 this->write("sk_VertexID");
1773 break;
1775 this->write("sk_InstanceID");
1776 break;
1778 // We'd set the front facing winding in the MTLRenderCommandEncoder to be counter
1779 // clockwise to match Skia convention.
1780 if (!fRTFlipName.empty()) {
1781 this->write("(" + fRTFlipName + ".y < 0 ? _frontFacing : !_frontFacing)");
1782 } else {
1783 this->write("_frontFacing");
1784 }
1785 break;
1789 } else {
1790 fContext.fErrors->error(ref.position(), "'sk_LastFragColor' not supported");
1791 }
1792 break;
1793 default:
1794 const Variable& var = *ref.variable();
1795 if (var.storage() == Variable::Storage::kGlobal) {
1796 if (is_input(var)) {
1797 this->write("_in.");
1798 } else if (is_output(var)) {
1799 this->write("_out.");
1800 } else if (is_uniforms(var)) {
1801 this->write("_uniforms.");
1802 } else if (is_threadgroup(var)) {
1803 this->write("_threadgroups.");
1804 } else if (is_in_globals(var)) {
1805 this->write("_globals.");
1806 }
1807 }
1808 this->writeName(var.mangledName());
1809 }
1810}
constexpr int SK_CLOCKWISE_BUILTIN
constexpr int SK_FRAGCOLOR_BUILTIN
constexpr int SK_LASTFRAGCOLOR_BUILTIN
constexpr int SK_SECONDARYFRAGCOLOR_BUILTIN
bool fDualSourceBlendingSupport
Definition SkSLUtil.h:84

◆ writeVarInitializer()

void SkSL::MetalCodeGenerator::writeVarInitializer ( const Variable var,
const Expression value 
)
protected

Definition at line 2770 of file SkSLMetalCodeGenerator.cpp.

2770 {
2771 this->writeExpression(value, Precedence::kExpression);
2772}

◆ writeWithIndexSubstitution()

void SkSL::MetalCodeGenerator::writeWithIndexSubstitution ( const std::function< void()> &  fn)
protected

Definition at line 568 of file SkSLMetalCodeGenerator.cpp.

568 {
569 auto oldIndexSubstitutionData = std::make_unique<IndexSubstitutionData>();
570 fIndexSubstitutionData.swap(oldIndexSubstitutionData);
571
572 // Invoke our helper function, with output going into our temporary stream.
573 {
574 AutoOutputStream outputToMainStream(this, &fIndexSubstitutionData->fMainStream);
575 fn();
576 }
577
578 if (fIndexSubstitutionData->fPrefixStream.bytesWritten() == 0) {
579 // Emit the main stream into the program as-is.
581 } else {
582 // Emit the prefix stream and main stream into the program as a sequence-expression.
583 // (Each prefix-expression must end with a comma.)
584 this->write("(");
587 this->write(")");
588 }
589
590 fIndexSubstitutionData.swap(oldIndexSubstitutionData);
591}

Member Data Documentation

◆ fAnonInterfaceCount

int SkSL::MetalCodeGenerator::fAnonInterfaceCount = 0
protected

Definition at line 325 of file SkSLMetalCodeGenerator.cpp.

◆ fAtLineStart

bool SkSL::MetalCodeGenerator::fAtLineStart = false
protected

Definition at line 333 of file SkSLMetalCodeGenerator.cpp.

◆ fCurrentFunction

const FunctionDeclaration* SkSL::MetalCodeGenerator::fCurrentFunction = nullptr
protected

Definition at line 340 of file SkSLMetalCodeGenerator.cpp.

◆ fExtraFunctionPrototypes

StringStream SkSL::MetalCodeGenerator::fExtraFunctionPrototypes
protected

Definition at line 330 of file SkSLMetalCodeGenerator.cpp.

◆ fExtraFunctions

StringStream SkSL::MetalCodeGenerator::fExtraFunctions
protected

Definition at line 329 of file SkSLMetalCodeGenerator.cpp.

◆ fFoundDerivatives

bool SkSL::MetalCodeGenerator::fFoundDerivatives = false
protected

Definition at line 335 of file SkSLMetalCodeGenerator.cpp.

◆ fFunctionHeader

std::string SkSL::MetalCodeGenerator::fFunctionHeader
protected

Definition at line 328 of file SkSLMetalCodeGenerator.cpp.

◆ fHelpers

skia_private::THashSet<std::string> SkSL::MetalCodeGenerator::fHelpers
protected

Definition at line 337 of file SkSLMetalCodeGenerator.cpp.

◆ fIndentation

int SkSL::MetalCodeGenerator::fIndentation = 0
protected

Definition at line 332 of file SkSLMetalCodeGenerator.cpp.

◆ fIndexSubstitutionData

std::unique_ptr<IndexSubstitutionData> SkSL::MetalCodeGenerator::fIndexSubstitutionData
protected

Definition at line 358 of file SkSLMetalCodeGenerator.cpp.

◆ fInterfaceBlockNameMap

skia_private::THashMap<const Type*, std::string> SkSL::MetalCodeGenerator::fInterfaceBlockNameMap
protected

Definition at line 324 of file SkSLMetalCodeGenerator.cpp.

◆ fLineEnding

const char* SkSL::MetalCodeGenerator::fLineEnding
protected

Definition at line 327 of file SkSLMetalCodeGenerator.cpp.

◆ fPaddingCount

int SkSL::MetalCodeGenerator::fPaddingCount = 0
protected

Definition at line 326 of file SkSLMetalCodeGenerator.cpp.

◆ fRequirements

skia_private::THashMap<const FunctionDeclaration*, Requirements> SkSL::MetalCodeGenerator::fRequirements
protected

Definition at line 336 of file SkSLMetalCodeGenerator.cpp.

◆ fReservedWords

skia_private::THashSet<std::string_view> SkSL::MetalCodeGenerator::fReservedWords
protected

Definition at line 323 of file SkSLMetalCodeGenerator.cpp.

◆ fRTFlipName

std::string SkSL::MetalCodeGenerator::fRTFlipName
protected

Definition at line 339 of file SkSLMetalCodeGenerator.cpp.

◆ fSwizzleHelperCount

int SkSL::MetalCodeGenerator::fSwizzleHelperCount = 0
protected

Definition at line 341 of file SkSLMetalCodeGenerator.cpp.

◆ fUniformBuffer

int SkSL::MetalCodeGenerator::fUniformBuffer = -1
protected

Definition at line 338 of file SkSLMetalCodeGenerator.cpp.

◆ fVarCount

int SkSL::MetalCodeGenerator::fVarCount = 0
protected

Definition at line 331 of file SkSLMetalCodeGenerator.cpp.

◆ fWrittenInverse2

bool SkSL::MetalCodeGenerator::fWrittenInverse2 = false
protected

Definition at line 361 of file SkSLMetalCodeGenerator.cpp.

◆ fWrittenInverse3

bool SkSL::MetalCodeGenerator::fWrittenInverse3 = false
protected

Definition at line 361 of file SkSLMetalCodeGenerator.cpp.

◆ fWrittenInverse4

bool SkSL::MetalCodeGenerator::fWrittenInverse4 = false
protected

Definition at line 361 of file SkSLMetalCodeGenerator.cpp.

◆ fWrittenMatrixCompMult

bool SkSL::MetalCodeGenerator::fWrittenMatrixCompMult = false
protected

Definition at line 362 of file SkSLMetalCodeGenerator.cpp.

◆ fWrittenOuterProduct

bool SkSL::MetalCodeGenerator::fWrittenOuterProduct = false
protected

Definition at line 363 of file SkSLMetalCodeGenerator.cpp.

◆ kFragCoord_Requirement

constexpr Requirements SkSL::MetalCodeGenerator::kFragCoord_Requirement = 1 << 4
staticconstexprprotected

Definition at line 114 of file SkSLMetalCodeGenerator.cpp.

◆ kGlobals_Requirement

constexpr Requirements SkSL::MetalCodeGenerator::kGlobals_Requirement = 1 << 3
staticconstexprprotected

Definition at line 113 of file SkSLMetalCodeGenerator.cpp.

◆ kInputs_Requirement

constexpr Requirements SkSL::MetalCodeGenerator::kInputs_Requirement = 1 << 0
staticconstexprprotected

Definition at line 110 of file SkSLMetalCodeGenerator.cpp.

◆ kInstanceID_Requirement

constexpr Requirements SkSL::MetalCodeGenerator::kInstanceID_Requirement = 1 << 7
staticconstexprprotected

Definition at line 117 of file SkSLMetalCodeGenerator.cpp.

◆ kNo_Requirements

constexpr Requirements SkSL::MetalCodeGenerator::kNo_Requirements = 0
staticconstexprprotected

Definition at line 109 of file SkSLMetalCodeGenerator.cpp.

◆ kOutputs_Requirement

constexpr Requirements SkSL::MetalCodeGenerator::kOutputs_Requirement = 1 << 1
staticconstexprprotected

Definition at line 111 of file SkSLMetalCodeGenerator.cpp.

◆ kSampleMaskIn_Requirement

constexpr Requirements SkSL::MetalCodeGenerator::kSampleMaskIn_Requirement = 1 << 5
staticconstexprprotected

Definition at line 115 of file SkSLMetalCodeGenerator.cpp.

◆ kSamplerSuffix

constexpr char SkSL::MetalCodeGenerator::kSamplerSuffix[] = "_Smplr"
staticconstexprprotected

Definition at line 343 of file SkSLMetalCodeGenerator.cpp.

◆ kTextureSuffix

constexpr char SkSL::MetalCodeGenerator::kTextureSuffix[] = "_Tex"
staticconstexprprotected

Definition at line 342 of file SkSLMetalCodeGenerator.cpp.

◆ kThreadgroups_Requirement

constexpr Requirements SkSL::MetalCodeGenerator::kThreadgroups_Requirement = 1 << 8
staticconstexprprotected

Definition at line 118 of file SkSLMetalCodeGenerator.cpp.

◆ kUniforms_Requirement

constexpr Requirements SkSL::MetalCodeGenerator::kUniforms_Requirement = 1 << 2
staticconstexprprotected

Definition at line 112 of file SkSLMetalCodeGenerator.cpp.

◆ kVertexID_Requirement

constexpr Requirements SkSL::MetalCodeGenerator::kVertexID_Requirement = 1 << 6
staticconstexprprotected

Definition at line 116 of file SkSLMetalCodeGenerator.cpp.


The documentation for this class was generated from the following file: