Flutter Engine
The Flutter Engine
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
 
virtual bool generateCode ()=0
 
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)
 
std::string splatMatrixOf1 (const Type &type)
 
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 94 of file SkSLMetalCodeGenerator.cpp.

Member Typedef Documentation

◆ IndexSubstitutionMap

Definition at line 353 of file SkSLMetalCodeGenerator.cpp.

◆ INHERITED

Definition at line 369 of file SkSLMetalCodeGenerator.cpp.

◆ Precedence

Definition at line 107 of file SkSLMetalCodeGenerator.cpp.

◆ Requirements

Definition at line 109 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 96 of file SkSLMetalCodeGenerator.cpp.

100 : INHERITED(context, caps, program, out)
101 , fReservedWords({"atan2", "rsqrt", "rint", "dfdx", "dfdy", "vertex", "fragment"})
102 , 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 1371 of file SkSLMetalCodeGenerator.cpp.

1373 {
1374 SkASSERT(rows <= 4);
1375 SkASSERT(columns <= 4);
1376
1377 std::string matrixType = this->typeName(ctor.type().componentType());
1378 size_t argIndex = 0;
1379 int argPosition = 0;
1380 auto args = ctor.argumentSpan();
1381
1382 static constexpr char kSwizzle[] = "xyzw";
1383 const char* separator = "";
1384 for (int c = 0; c < columns; ++c) {
1385 fExtraFunctions.printf("%s%s%d(", separator, matrixType.c_str(), rows);
1386 separator = "), ";
1387
1388 const char* columnSeparator = "";
1389 for (int r = 0; r < rows;) {
1390 fExtraFunctions.writeText(columnSeparator);
1391 columnSeparator = ", ";
1392
1393 if (argIndex < args.size()) {
1394 const Type& argType = args[argIndex]->type();
1395 switch (argType.typeKind()) {
1397 fExtraFunctions.printf("x%zu", argIndex);
1398 ++r;
1399 ++argPosition;
1400 break;
1401 }
1403 fExtraFunctions.printf("x%zu.", argIndex);
1404 do {
1405 fExtraFunctions.write8(kSwizzle[argPosition]);
1406 ++r;
1407 ++argPosition;
1408 } while (r < rows && argPosition < argType.columns());
1409 break;
1410 }
1412 fExtraFunctions.printf("x%zu[%d].", argIndex, argPosition / argType.rows());
1413 do {
1414 fExtraFunctions.write8(kSwizzle[argPosition]);
1415 ++r;
1416 ++argPosition;
1417 } while (r < rows && (argPosition % argType.rows()) != 0);
1418 break;
1419 }
1420 default: {
1421 SkDEBUGFAIL("incorrect type of argument for matrix constructor");
1422 fExtraFunctions.writeText("<error>");
1423 break;
1424 }
1425 }
1426
1427 if (argPosition >= argType.columns() * argType.rows()) {
1428 ++argIndex;
1429 argPosition = 0;
1430 }
1431 } else {
1432 SkDEBUGFAIL("not enough arguments for matrix constructor");
1433 fExtraFunctions.writeText("<error>");
1434 }
1435 }
1436 }
1437
1438 if (argPosition != 0 || argIndex != args.size()) {
1439 SkDEBUGFAIL("incorrect number of arguments for matrix constructor");
1440 fExtraFunctions.writeText(", <error>");
1441 }
1442
1444}
#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 1331 of file SkSLMetalCodeGenerator.cpp.

1331 {
1332 SkASSERT(rows <= 4);
1333 SkASSERT(columns <= 4);
1334
1335 std::string matrixType = this->typeName(sourceMatrix.componentType());
1336
1337 const char* separator = "";
1338 for (int c = 0; c < columns; ++c) {
1339 fExtraFunctions.printf("%s%s%d(", separator, matrixType.c_str(), rows);
1340 separator = "), ";
1341
1342 // Determine how many values to take from the source matrix for this row.
1343 int swizzleLength = 0;
1344 if (c < sourceMatrix.columns()) {
1345 swizzleLength = std::min<>(rows, sourceMatrix.rows());
1346 }
1347
1348 // Emit all the values from the source matrix row.
1349 bool firstItem;
1350 switch (swizzleLength) {
1351 case 0: firstItem = true; break;
1352 case 1: firstItem = false; fExtraFunctions.printf("x0[%d].x", c); break;
1353 case 2: firstItem = false; fExtraFunctions.printf("x0[%d].xy", c); break;
1354 case 3: firstItem = false; fExtraFunctions.printf("x0[%d].xyz", c); break;
1355 case 4: firstItem = false; fExtraFunctions.printf("x0[%d].xyzw", c); break;
1356 default: SkUNREACHABLE;
1357 }
1358
1359 // Emit the placeholder identity-matrix cells.
1360 for (int r = swizzleLength; r < rows; ++r) {
1361 fExtraFunctions.printf("%s%s", firstItem ? "" : ", ", (r == c) ? "1.0" : "0.0");
1362 firstItem = false;
1363 }
1364 }
1365
1367}
#define SkUNREACHABLE
Definition: SkAssert.h:135

◆ finishLine()

void SkSL::MetalCodeGenerator::finishLine ( )
protected

Definition at line 416 of file SkSLMetalCodeGenerator.cpp.

416 {
417 if (!fAtLineStart) {
418 this->writeLine();
419 }
420}
void writeLine(std::string_view s=std::string_view())

◆ generateCode()

bool SkSL::MetalCodeGenerator::generateCode ( )
overridevirtual

Implements SkSL::CodeGenerator.

Definition at line 3613 of file SkSLMetalCodeGenerator.cpp.

3613 {
3614 StringStream header;
3615 {
3616 AutoOutputStream outputToHeader(this, &header, &fIndentation);
3617 this->writeHeader();
3618 this->writeConstantVariables();
3619 this->writeSampler2DPolyfill();
3620 this->writeStructDefinitions();
3621 this->writeUniformStruct();
3622 this->writeInputStruct();
3624 this->writeOutputStruct();
3625 }
3626 this->writeInterfaceBlocks();
3627 this->writeGlobalStruct();
3628 this->writeThreadgroupStruct();
3629
3630 // Emit prototypes for every built-in function; these aren't always added in perfect order.
3631 for (const ProgramElement* e : fProgram.fSharedElements) {
3632 if (e->is<FunctionDefinition>()) {
3633 this->writeFunctionDeclaration(e->as<FunctionDefinition>().declaration());
3634 this->writeLine(";");
3635 }
3636 }
3637 }
3638 StringStream body;
3639 {
3640 AutoOutputStream outputToBody(this, &body, &fIndentation);
3641
3642 for (const ProgramElement* e : fProgram.elements()) {
3643 this->writeProgramElement(*e);
3644 }
3645 }
3649 write_stringstream(body, *fOut);
3650 return fContext.fErrors->errorCount() == 0;
3651}
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:42
static const char header[]
Definition: skpbench.cpp:88
static bool IsCompute(ProgramKind kind)
ElementsCollection elements() const
Definition: SkSLProgram.h:140
std::vector< const ProgramElement * > fSharedElements
Definition: SkSLProgram.h:164
std::unique_ptr< ProgramConfig > fConfig
Definition: SkSLProgram.h:153

◆ getBitcastIntrinsic()

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

Definition at line 568 of file SkSLMetalCodeGenerator.cpp.

568 {
569 return "as_type<" + outType.displayName() + ">";
570}

◆ getInversePolyfill()

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

Definition at line 797 of file SkSLMetalCodeGenerator.cpp.

797 {
798 // Only use polyfills for a function taking a single-argument square matrix.
799 SkASSERT(arguments.size() == 1);
800 const Type& type = arguments.front()->type();
801 if (type.isMatrix() && type.rows() == type.columns()) {
802 switch (type.rows()) {
803 case 2:
804 if (!fWrittenInverse2) {
805 fWrittenInverse2 = true;
807 }
808 return "mat2_inverse";
809 case 3:
810 if (!fWrittenInverse3) {
811 fWrittenInverse3 = true;
813 }
814 return "mat3_inverse";
815 case 4:
816 if (!fWrittenInverse4) {
817 fWrittenInverse4 = true;
819 }
820 return "mat4_inverse";
821 }
822 }
823 SkDEBUGFAILF("no polyfill for inverse(%s)", type.description().c_str());
824 return "inverse";
825}
#define SkDEBUGFAILF(fmt,...)
Definition: SkAssert.h:119
GLenum type
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 1450 of file SkSLMetalCodeGenerator.cpp.

1450 {
1451 const Type& type = c.type();
1452 int columns = type.columns();
1453 int rows = type.rows();
1454 auto args = c.argumentSpan();
1455 std::string typeName = this->typeName(type);
1456
1457 // Create the helper-method name and use it as our lookup key.
1458 std::string name = String::printf("%s_from", typeName.c_str());
1459 for (const std::unique_ptr<Expression>& expr : args) {
1460 String::appendf(&name, "_%s", this->typeName(expr->type()).c_str());
1461 }
1462
1463 // If a helper-method has not been synthesized yet, create it now.
1464 if (!fHelpers.contains(name)) {
1465 fHelpers.add(name);
1466
1467 // Unlike GLSL, Metal requires that matrices are initialized with exactly R vectors of C
1468 // components apiece. (In Metal 2.0, you can also supply R*C scalars, but you still cannot
1469 // supply a mixture of scalars and vectors.)
1470 fExtraFunctions.printf("%s %s(", typeName.c_str(), name.c_str());
1471
1472 size_t argIndex = 0;
1473 const char* argSeparator = "";
1474 for (const std::unique_ptr<Expression>& expr : args) {
1475 fExtraFunctions.printf("%s%s x%zu", argSeparator,
1476 this->typeName(expr->type()).c_str(), argIndex++);
1477 argSeparator = ", ";
1478 }
1479
1480 fExtraFunctions.printf(") {\n return %s(", typeName.c_str());
1481
1482 if (args.size() == 1 && args.front()->type().isMatrix()) {
1483 this->assembleMatrixFromMatrix(args.front()->type(), columns, rows);
1484 } else {
1485 this->assembleMatrixFromExpressions(c, columns, rows);
1486 }
1487
1488 fExtraFunctions.writeText(");\n}\n");
1489 }
1490 return name;
1491}
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:592
bool contains(const T &item) const
Definition: SkTHash.h:595
std::string printf(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: SkSLString.cpp:83
std::string void appendf(std::string *str, const char *fmt,...) SK_PRINTF_LIKE(2
Definition: SkSLString.cpp:92
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32

◆ getTempVariable()

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

Definition at line 856 of file SkSLMetalCodeGenerator.cpp.

856 {
857 std::string tempVar = "_skTemp" + std::to_string(fVarCount++);
858 this->fFunctionHeader += " " + this->typeName(type) + " " + tempVar + ";\n";
859 return tempVar;
860}
static SkString to_string(int n)
Definition: nanobench.cpp:119

◆ getUniformBinding()

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

Definition at line 2455 of file SkSLMetalCodeGenerator.cpp.

2455 {
2456 return (layout.fBinding >= 0) ? layout.fBinding
2457 : fProgram.fConfig->fSettings.fDefaultUniformBinding;
2458}

◆ getUniformSet()

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

Definition at line 2460 of file SkSLMetalCodeGenerator.cpp.

2460 {
2461 return (layout.fSet >= 0) ? layout.fSet
2462 : fProgram.fConfig->fSettings.fDefaultUniformSet;
2463}

◆ getVectorFromMat2x2ConstructorHelper()

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

Definition at line 1589 of file SkSLMetalCodeGenerator.cpp.

1589 {
1590 SkASSERT(matrixType.isMatrix());
1591 SkASSERT(matrixType.rows() == 2);
1592 SkASSERT(matrixType.columns() == 2);
1593
1594 std::string baseType = this->typeName(matrixType.componentType());
1595 std::string name = String::printf("%s4_from_%s2x2", baseType.c_str(), baseType.c_str());
1596 if (!fHelpers.contains(name)) {
1597 fHelpers.add(name);
1598
1600%s4 %s(%s2x2 x) {
1601 return %s4(x[0].xy, x[1].xy);
1602}
1603)", baseType.c_str(), name.c_str(), baseType.c_str(), baseType.c_str());
1604 }
1605
1606 return name;
1607}

◆ matrixConstructHelperIsNeeded()

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

Definition at line 1493 of file SkSLMetalCodeGenerator.cpp.

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

◆ requirements() [1/2]

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

Definition at line 3584 of file SkSLMetalCodeGenerator.cpp.

3584 {
3585 Requirements* found = fRequirements.find(&f);
3586 if (!found) {
3588 for (const ProgramElement* e : fProgram.elements()) {
3589 if (e->is<FunctionDefinition>()) {
3590 const FunctionDefinition& def = e->as<FunctionDefinition>();
3591 if (&def.declaration() == &f) {
3592 Requirements reqs = this->requirements(def.body().get());
3593 fRequirements.set(&f, reqs);
3594 return reqs;
3595 }
3596 }
3597 }
3598
3599 // Loading data from the instrinsic float buffer requires access to the Globals struct.
3600 if (f.intrinsicKind() == IntrinsicKind::k_loadFloatBuffer_IntrinsicKind) {
3602 fRequirements.set(&f, reqs);
3603 return reqs;
3604 }
3605
3606 // We never found a definition for this declared function, but it's legal to prototype a
3607 // function without ever giving a definition, as long as you don't call it.
3608 return kNo_Requirements;
3609 }
3610 return *found;
3611}
skia_private::THashMap< const FunctionDeclaration *, Requirements > fRequirements
static constexpr Requirements kGlobals_Requirement
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 3517 of file SkSLMetalCodeGenerator.cpp.

3517 {
3518 class RequirementsVisitor : public ProgramVisitor {
3519 public:
3521
3522 bool visitExpression(const Expression& e) override {
3523 switch (e.kind()) {
3524 case Expression::Kind::kFunctionCall: {
3525 const FunctionCall& f = e.as<FunctionCall>();
3526 fRequirements |= fCodeGen->requirements(f.function());
3527 break;
3528 }
3529 case Expression::Kind::kFieldAccess: {
3530 const FieldAccess& f = e.as<FieldAccess>();
3531 if (f.ownerKind() == FieldAccess::OwnerKind::kAnonymousInterfaceBlock) {
3533 return false; // don't recurse into the base variable
3534 }
3535 break;
3536 }
3537 case Expression::Kind::kVariableReference: {
3538 const Variable& var = *e.as<VariableReference>().variable();
3539
3540 if (var.layout().fBuiltin == SK_FRAGCOORD_BUILTIN) {
3542 } else if (var.layout().fBuiltin == SK_SAMPLEMASKIN_BUILTIN) {
3544 } else if (var.layout().fBuiltin == SK_SAMPLEMASK_BUILTIN) {
3546 } else if (var.layout().fBuiltin == SK_VERTEXID_BUILTIN) {
3548 } else if (var.layout().fBuiltin == SK_INSTANCEID_BUILTIN) {
3550 } else if (var.storage() == Variable::Storage::kGlobal) {
3551 if (is_input(var)) {
3553 } else if (is_output(var)) {
3555 } else if (is_uniforms(var)) {
3557 } else if (is_threadgroup(var)) {
3559 } else if (is_in_globals(var)) {
3561 }
3562 }
3563 break;
3564 }
3565 default:
3566 break;
3567 }
3568 return INHERITED::visitExpression(e);
3569 }
3570
3571 MetalCodeGenerator* fCodeGen;
3573 using INHERITED = ProgramVisitor;
3574 };
3575
3576 RequirementsVisitor visitor;
3577 if (s) {
3578 visitor.fCodeGen = this;
3579 visitor.visitStatement(*s);
3580 }
3581 return visitor.fRequirements;
3582}
constexpr int SK_SAMPLEMASK_BUILTIN
Definition: SkSLCompiler.h:33
constexpr int SK_VERTEXID_BUILTIN
Definition: SkSLCompiler.h:35
constexpr int SK_INSTANCEID_BUILTIN
Definition: SkSLCompiler.h:36
constexpr int SK_FRAGCOORD_BUILTIN
Definition: SkSLCompiler.h:30
constexpr int SK_SAMPLEMASKIN_BUILTIN
Definition: SkSLCompiler.h:32
static constexpr Requirements kFragCoord_Requirement
static constexpr Requirements kVertexID_Requirement
static constexpr Requirements kInputs_Requirement
static constexpr Requirements kOutputs_Requirement
static constexpr Requirements kSampleMaskIn_Requirement
static constexpr Requirements kUniforms_Requirement
static constexpr Requirements kInstanceID_Requirement
static constexpr Requirements kThreadgroups_Requirement
MetalCodeGenerator(const Context *context, const ShaderCaps *caps, const Program *program, OutputStream *out)
virtual bool visitStatement(typename T::Statement &statement)
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

◆ splatMatrixOf1()

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

Definition at line 2107 of file SkSLMetalCodeGenerator.cpp.

2107 {
2108 std::string str = this->typeName(type) + '(';
2109
2110 auto separator = SkSL::String::Separator();
2111 for (int index = type.slotCount(); index--;) {
2112 str += separator();
2113 str += "1.0";
2114 }
2115
2116 return str + ')';
2117}
std::string void void auto Separator()
Definition: SkSLString.h:30

◆ typeName()

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

Definition at line 426 of file SkSLMetalCodeGenerator.cpp.

426 {
427 // we need to know the modifiers for textures
428 const Type& type = raw.resolve().scalarTypeForLiteral();
429 switch (type.typeKind()) {
431 SkASSERT(!type.isUnsizedArray());
432 SkASSERTF(type.columns() > 0, "invalid array size: %s", type.description().c_str());
433 return String::printf("array<%s, %d>",
434 this->typeName(type.componentType()).c_str(), type.columns());
435
437 return this->typeName(type.componentType()) + std::to_string(type.columns());
438
440 return this->typeName(type.componentType()) + std::to_string(type.columns()) + "x" +
441 std::to_string(type.rows());
442
444 if (type.dimensions() != SpvDim2D) {
445 fContext.fErrors->error(Position(), "Unsupported texture dimensions");
446 }
447 return "sampler2D";
448
450 switch (type.textureAccess()) {
451 case Type::TextureAccess::kSample: return "texture2d<half>";
452 case Type::TextureAccess::kRead: return "texture2d<half, access::read>";
453 case Type::TextureAccess::kWrite: return "texture2d<half, access::write>";
454 case Type::TextureAccess::kReadWrite: return "texture2d<half, access::read_write>";
455 default: break;
456 }
458
460 // SkSL currently only supports the atomicUint type.
462 return "atomic_uint";
463
464 default:
465 return std::string(type.name());
466 }
467}
#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 3253 of file SkSLMetalCodeGenerator.cpp.

3253 {
3254 for (const ProgramElement* element : fProgram.elements()) {
3255 if (element->is<InterfaceBlock>()) {
3256 const auto* ib = &element->as<InterfaceBlock>();
3257 if (ib->typeName() != "sk_PerVertex") {
3258 visitor->visitInterfaceBlock(*ib, fInterfaceBlockNameMap[&ib->var()->type()]);
3259 }
3260 continue;
3261 }
3262 if (!element->is<GlobalVarDeclaration>()) {
3263 continue;
3264 }
3265 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
3266 const VarDeclaration& decl = global.varDeclaration();
3267 const Variable& var = *decl.var();
3268 if (decl.baseType().typeKind() == Type::TypeKind::kSampler) {
3269 visitor->visitSampler(var.type(), var.mangledName());
3270 continue;
3271 }
3272 if (decl.baseType().typeKind() == Type::TypeKind::kTexture) {
3273 visitor->visitTexture(var.type(), var.mangledName());
3274 continue;
3275 }
3276 if (!(var.modifierFlags() & ~ModifierFlag::kConst) && var.layout().fBuiltin == -1) {
3277 if (is_in_globals(var)) {
3278 // Visit a regular global variable.
3279 visitor->visitNonconstantVariable(var, decl.value().get());
3280 } else {
3281 // Visit a constant-expression variable.
3282 SkASSERT(var.modifierFlags().isConst());
3283 visitor->visitConstantVariable(decl);
3284 }
3285 }
3286 }
3287}
skia_private::THashMap< const Type *, std::string > fInterfaceBlockNameMap
ModifierFlag

◆ visitThreadgroupStruct()

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

Definition at line 3410 of file SkSLMetalCodeGenerator.cpp.

3410 {
3411 for (const ProgramElement* element : fProgram.elements()) {
3412 if (!element->is<GlobalVarDeclaration>()) {
3413 continue;
3414 }
3415 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
3416 const VarDeclaration& decl = global.varDeclaration();
3417 const Variable& var = *decl.var();
3418 if (var.modifierFlags().isWorkgroup()) {
3419 SkASSERT(!decl.value());
3420 SkASSERT(!var.modifierFlags().isConst());
3421 visitor->visitNonconstantVariable(var);
3422 }
3423 }
3424}

◆ write()

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

Definition at line 395 of file SkSLMetalCodeGenerator.cpp.

395 {
396 if (s.empty()) {
397 return;
398 }
399#if defined(SK_DEBUG) || defined(SKSL_STANDALONE)
400 if (fAtLineStart) {
401 for (int i = 0; i < fIndentation; i++) {
402 fOut->writeText(" ");
403 }
404 }
405#endif
406 fOut->writeText(std::string(s).c_str());
407 fAtLineStart = false;
408}
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 1679 of file SkSLMetalCodeGenerator.cpp.

1682 {
1683 this->writeType(c.type());
1684 this->write(leftBracket);
1685 const char* separator = "";
1686 for (const std::unique_ptr<Expression>& arg : c.argumentSpan()) {
1687 this->write(separator);
1688 separator = ", ";
1689 this->writeExpression(*arg, Precedence::kSequence);
1690 }
1691 this->write(rightBracket);
1692}
void writeExpression(const Expression &expr, Precedence parentPrecedence)
void writeType(const Type &type)
void write(std::string_view s)

◆ writeArgumentList()

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

Definition at line 868 of file SkSLMetalCodeGenerator.cpp.

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

◆ writeArrayEqualityHelpers()

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

Definition at line 2000 of file SkSLMetalCodeGenerator.cpp.

2000 {
2001 SkASSERT(type.isArray());
2002
2003 // If the array's component type needs a helper as well, we need to emit that one first.
2004 this->writeEqualityHelpers(type.componentType(), type.componentType());
2005
2006 std::string key = "ArrayEquality []";
2007 if (!fHelpers.contains(key)) {
2008 fHelpers.add(key);
2010template <typename T1, typename T2>
2011bool operator==(const array_ref<T1> left, const array_ref<T2> right);
2012template <typename T1, typename T2>
2013bool operator!=(const array_ref<T1> left, const array_ref<T2> right);
2014)");
2016template <typename T1, typename T2>
2017bool operator==(const array_ref<T1> left, const array_ref<T2> right) {
2018 if (left.size() != right.size()) {
2019 return false;
2020 }
2021 for (size_t index = 0; index < left.size(); ++index) {
2022 if (!all(left[index] == right[index])) {
2023 return false;
2024 }
2025 }
2026 return true;
2027}
2028
2029template <typename T1, typename T2>
2030bool operator!=(const array_ref<T1> left, const array_ref<T2> right) {
2031 return !(left == right);
2032}
2033)");
2034 }
2035}
void writeEqualityHelpers(const Type &leftType, const Type &rightType)

◆ writeBinaryExpression()

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

Definition at line 2149 of file SkSLMetalCodeGenerator.cpp.

2150 {
2151 const Expression& left = *b.left();
2152 const Expression& right = *b.right();
2153 const Type& leftType = left.type();
2154 const Type& rightType = right.type();
2155 Operator op = b.getOperator();
2156 Precedence precedence = op.getBinaryPrecedence();
2157 bool needParens = precedence >= parentPrecedence;
2158 switch (op.kind()) {
2159 case Operator::Kind::EQEQ:
2160 this->writeEqualityHelpers(leftType, rightType);
2161 if (leftType.isVector()) {
2162 this->write("all");
2163 needParens = true;
2164 }
2165 break;
2166 case Operator::Kind::NEQ:
2167 this->writeEqualityHelpers(leftType, rightType);
2168 if (leftType.isVector()) {
2169 this->write("any");
2170 needParens = true;
2171 }
2172 break;
2173 default:
2174 break;
2175 }
2176 if (leftType.isMatrix() && rightType.isMatrix() && op.kind() == Operator::Kind::STAREQ) {
2177 this->writeMatrixTimesEqualHelper(leftType, rightType, b.type());
2178 }
2179 if (op.removeAssignment().kind() == Operator::Kind::SLASH &&
2180 ((leftType.isMatrix() && rightType.isMatrix()) ||
2181 (leftType.isScalar() && rightType.isMatrix()) ||
2182 (leftType.isMatrix() && rightType.isScalar()))) {
2183 this->writeMatrixDivisionHelpers(leftType.isMatrix() ? leftType : rightType);
2184 }
2185
2186 if (needParens) {
2187 this->write("(");
2188 }
2189
2190 // Some expressions need to be rewritten from `lhs *= rhs` to `lhs = lhs * rhs`, e.g.:
2191 // float4 x = float4(1);
2192 // x.xy *= float2x2(...);
2193 // will report the error "non-const reference cannot bind to vector element."
2194 if (op.isCompoundAssignment() && left.kind() == Expression::Kind::kSwizzle) {
2195 // We need to do the rewrite. This could be dangerous if the lhs contains an index
2196 // expression with a side effect (such as `array[Func()]`), so we enable index-substitution
2197 // here for the LHS; any index-expression with side effects will be evaluated into a scratch
2198 // variable.
2199 this->writeWithIndexSubstitution([&] {
2200 this->writeExpression(left, precedence);
2201 this->write(" = ");
2202 this->writeExpression(left, Precedence::kAssignment);
2203 this->write(operator_name(op.removeAssignment()));
2204
2205 // We never want to create index-expression substitutes on the RHS of the expression;
2206 // the RHS is only emitted one time.
2207 fIndexSubstitutionData->fCreateSubstitutes = false;
2208
2209 this->writeBinaryExpressionElement(right, op, left,
2210 op.removeAssignment().getBinaryPrecedence());
2211 });
2212 } else {
2213 // We don't need any rewrite; emit the binary expression as-is.
2214 this->writeBinaryExpressionElement(left, op, right, precedence);
2215 this->write(operator_name(op));
2216 this->writeBinaryExpressionElement(right, op, left, precedence);
2217 }
2218
2219 if (needParens) {
2220 this->write(")");
2221 }
2222}
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 2131 of file SkSLMetalCodeGenerator.cpp.

2134 {
2135 bool needMatrixSplatOnScalar = other.type().isMatrix() && expr.type().isNumber() &&
2136 op.isValidForMatrixOrVector() &&
2137 op.removeAssignment().kind() != Operator::Kind::STAR;
2138 if (needMatrixSplatOnScalar) {
2139 this->writeNumberAsMatrix(expr, other.type());
2140 } else if (op.isEquality() && expr.type().isArray()) {
2141 this->write("make_array_ref(");
2142 this->writeExpression(expr, precedence);
2143 this->write(")");
2144 } else {
2145 this->writeExpression(expr, precedence);
2146 }
2147}
void writeNumberAsMatrix(const Expression &expr, const Type &matrixType)

◆ writeBlock()

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

Definition at line 2902 of file SkSLMetalCodeGenerator.cpp.

2902 {
2903 // Write scope markers if this block is a scope, or if the block is empty (since we need to emit
2904 // something here to make the code valid).
2905 bool isScope = b.isScope() || b.isEmpty();
2906 if (isScope) {
2907 this->writeLine("{");
2908 fIndentation++;
2909 }
2910 for (const std::unique_ptr<Statement>& stmt : b.children()) {
2911 if (!stmt->isEmpty()) {
2912 this->writeStatement(*stmt);
2913 this->finishLine();
2914 }
2915 }
2916 if (isScope) {
2917 fIndentation--;
2918 this->write("}");
2919 }
2920}
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 1694 of file SkSLMetalCodeGenerator.cpp.

1697 {
1698 return this->writeAnyConstructor(c, leftBracket, rightBracket, parentPrecedence);
1699}
void writeAnyConstructor(const AnyConstructor &c, const char *leftBracket, const char *rightBracket, Precedence parentPrecedence)

◆ writeComputeMainInputs()

void SkSL::MetalCodeGenerator::writeComputeMainInputs ( )
protected

Definition at line 2658 of file SkSLMetalCodeGenerator.cpp.

2658 {
2659 // Compute shaders only have input variables (e.g. sk_GlobalInvocationID) and access program
2660 // inputs/outputs via the Globals and Uniforms structs. We collect the allowed "in" parameters
2661 // into an Input struct here, since the rest of the code expects the normal _in / _out pattern.
2662 this->write("Inputs _in = { ");
2663 const char* separator = "";
2664 for (const ProgramElement* e : fProgram.elements()) {
2665 if (e->is<GlobalVarDeclaration>()) {
2666 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
2667 const Variable* var = decls.varDeclaration().var();
2668 if (is_input(*var)) {
2669 this->write(separator);
2670 separator = ", ";
2671 this->writeName(var->mangledName());
2672 }
2673 }
2674 }
2675 this->writeLine(" };");
2676}
void writeName(std::string_view name)

◆ writeConstantVariables()

void SkSL::MetalCodeGenerator::writeConstantVariables ( )
protected

Definition at line 3237 of file SkSLMetalCodeGenerator.cpp.

3237 {
3238 class : public GlobalStructVisitor {
3239 public:
3240 void visitConstantVariable(const VarDeclaration& decl) override {
3241 fCodeGen->write("constant ");
3242 fCodeGen->writeVarDeclaration(decl);
3243 fCodeGen->finishLine();
3244 }
3245
3246 MetalCodeGenerator* fCodeGen = nullptr;
3247 } visitor;
3248
3249 visitor.fCodeGen = this;
3250 this->visitGlobalStruct(&visitor);
3251}
void visitGlobalStruct(GlobalStructVisitor *visitor)

◆ writeConstructorArrayCast()

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

Definition at line 1558 of file SkSLMetalCodeGenerator.cpp.

1559 {
1560 const Type& inType = c.argument()->type().componentType();
1561 const Type& outType = c.type().componentType();
1562 std::string inTypeName = this->typeName(inType);
1563 std::string outTypeName = this->typeName(outType);
1564
1565 std::string name = "array_of_" + outTypeName + "_from_" + inTypeName;
1566 if (!fHelpers.contains(name)) {
1567 fHelpers.add(name);
1569template <size_t N>
1570array<%s, N> %s(thread const array<%s, N>& x) {
1571 array<%s, N> result;
1572 for (int i = 0; i < N; ++i) {
1573 result[i] = %s(x[i]);
1574 }
1575 return result;
1576}
1577)",
1578 outTypeName.c_str(), name.c_str(), inTypeName.c_str(),
1579 outTypeName.c_str(),
1580 outTypeName.c_str());
1581 }
1582
1583 this->write(name);
1584 this->write("(");
1585 this->writeExpression(*c.argument(), Precedence::kSequence);
1586 this->write(")");
1587}

◆ writeConstructorCompound()

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

Definition at line 1547 of file SkSLMetalCodeGenerator.cpp.

1548 {
1549 if (c.type().isVector()) {
1550 this->writeConstructorCompoundVector(c, parentPrecedence);
1551 } else if (c.type().isMatrix()) {
1552 this->writeConstructorCompoundMatrix(c, parentPrecedence);
1553 } else {
1554 fContext.fErrors->error(c.fPosition, "unsupported compound constructor");
1555 }
1556}
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 1629 of file SkSLMetalCodeGenerator.cpp.

1630 {
1631 SkASSERT(c.type().isMatrix());
1632
1633 // Emit and invoke a matrix-constructor helper method if one is necessary.
1634 if (this->matrixConstructHelperIsNeeded(c)) {
1635 this->write(this->getMatrixConstructHelper(c));
1636 this->write("(");
1637 const char* separator = "";
1638 for (const std::unique_ptr<Expression>& expr : c.arguments()) {
1639 this->write(separator);
1640 separator = ", ";
1641 this->writeExpression(*expr, Precedence::kSequence);
1642 }
1643 this->write(")");
1644 return;
1645 }
1646
1647 // Metal doesn't allow creating matrices by passing in scalars and vectors in a jumble; it
1648 // requires your scalars to be grouped up into columns. Because `matrixConstructHelperIsNeeded`
1649 // returned false, we know that none of our scalars/vectors "wrap" across across a column, so we
1650 // can group our inputs up and synthesize a constructor for each column.
1651 const Type& matrixType = c.type();
1652 const Type& columnType = matrixType.columnType(fContext);
1653
1654 this->writeType(matrixType);
1655 this->write("(");
1656 const char* separator = "";
1657 int scalarCount = 0;
1658 for (const std::unique_ptr<Expression>& arg : c.arguments()) {
1659 this->write(separator);
1660 separator = ", ";
1661 if (arg->type().columns() < matrixType.rows()) {
1662 // Write a `floatN(` constructor to group scalars and smaller vectors together.
1663 if (!scalarCount) {
1664 this->writeType(columnType);
1665 this->write("(");
1666 }
1667 scalarCount += arg->type().columns();
1668 }
1669 this->writeExpression(*arg, Precedence::kSequence);
1670 if (scalarCount && scalarCount == matrixType.rows()) {
1671 // Close our `floatN(...` constructor block from above.
1672 this->write(")");
1673 scalarCount = 0;
1674 }
1675 }
1676 this->write(")");
1677}
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 1609 of file SkSLMetalCodeGenerator.cpp.

1610 {
1611 SkASSERT(c.type().isVector());
1612
1613 // Metal supports constructing vectors from a mix of scalars and vectors, but not matrices.
1614 // GLSL supports vec4(mat2x2), so we detect that case here and emit a helper function.
1615 if (c.type().columns() == 4 && c.argumentSpan().size() == 1) {
1616 const Expression& expr = *c.argumentSpan().front();
1617 if (expr.type().isMatrix()) {
1618 this->write(this->getVectorFromMat2x2ConstructorHelper(expr.type()));
1619 this->write("(");
1620 this->writeExpression(expr, Precedence::kSequence);
1621 this->write(")");
1622 return;
1623 }
1624 }
1625
1626 this->writeAnyConstructor(c, "(", ")", parentPrecedence);
1627}
std::string getVectorFromMat2x2ConstructorHelper(const Type &matrixType)

◆ writeConstructorMatrixResize()

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

Definition at line 1537 of file SkSLMetalCodeGenerator.cpp.

1538 {
1539 // Matrix-resize via casting doesn't natively exist in Metal at all, so we always need to use a
1540 // matrix-construct helper here.
1541 this->write(this->getMatrixConstructHelper(c));
1542 this->write("(");
1543 this->writeExpression(*c.argument(), Precedence::kSequence);
1544 this->write(")");
1545}

◆ writeDoStatement()

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

Definition at line 2960 of file SkSLMetalCodeGenerator.cpp.

2960 {
2961 this->write("do ");
2962 this->writeStatement(*d.statement());
2963 this->write(" while (");
2964 this->writeExpression(*d.test(), Precedence::kExpression);
2965 this->write(");");
2966}
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 2092 of file SkSLMetalCodeGenerator.cpp.

2092 {
2093 if (leftType.isArray() && rightType.isArray()) {
2094 this->writeArrayEqualityHelpers(leftType);
2095 return;
2096 }
2097 if (leftType.isStruct() && rightType.isStruct()) {
2098 this->writeStructEqualityHelpers(leftType);
2099 return;
2100 }
2101 if (leftType.isMatrix() && rightType.isMatrix()) {
2102 this->writeMatrixEqualityHelpers(leftType, rightType);
2103 return;
2104 }
2105}
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 482 of file SkSLMetalCodeGenerator.cpp.

482 {
483 switch (expr.kind()) {
484 case Expression::Kind::kBinary:
485 this->writeBinaryExpression(expr.as<BinaryExpression>(), parentPrecedence);
486 break;
487 case Expression::Kind::kConstructorArray:
488 case Expression::Kind::kConstructorStruct:
489 this->writeAnyConstructor(expr.asAnyConstructor(), "{", "}", parentPrecedence);
490 break;
491 case Expression::Kind::kConstructorArrayCast:
492 this->writeConstructorArrayCast(expr.as<ConstructorArrayCast>(), parentPrecedence);
493 break;
494 case Expression::Kind::kConstructorCompound:
495 this->writeConstructorCompound(expr.as<ConstructorCompound>(), parentPrecedence);
496 break;
497 case Expression::Kind::kConstructorDiagonalMatrix:
498 case Expression::Kind::kConstructorSplat:
499 this->writeAnyConstructor(expr.asAnyConstructor(), "(", ")", parentPrecedence);
500 break;
501 case Expression::Kind::kConstructorMatrixResize:
502 this->writeConstructorMatrixResize(expr.as<ConstructorMatrixResize>(),
503 parentPrecedence);
504 break;
505 case Expression::Kind::kConstructorScalarCast:
506 case Expression::Kind::kConstructorCompoundCast:
507 this->writeCastConstructor(expr.asAnyConstructor(), "(", ")", parentPrecedence);
508 break;
510 this->write("false");
511 break;
512 case Expression::Kind::kFieldAccess:
513 this->writeFieldAccess(expr.as<FieldAccess>());
514 break;
516 this->writeLiteral(expr.as<Literal>());
517 break;
518 case Expression::Kind::kFunctionCall:
519 this->writeFunctionCall(expr.as<FunctionCall>());
520 break;
521 case Expression::Kind::kPrefix:
522 this->writePrefixExpression(expr.as<PrefixExpression>(), parentPrecedence);
523 break;
524 case Expression::Kind::kPostfix:
525 this->writePostfixExpression(expr.as<PostfixExpression>(), parentPrecedence);
526 break;
527 case Expression::Kind::kSetting:
528 this->writeExpression(*expr.as<Setting>().toLiteral(fCaps), parentPrecedence);
529 break;
530 case Expression::Kind::kSwizzle:
531 this->writeSwizzle(expr.as<Swizzle>());
532 break;
533 case Expression::Kind::kVariableReference:
534 this->writeVariableReference(expr.as<VariableReference>());
535 break;
536 case Expression::Kind::kTernary:
537 this->writeTernaryExpression(expr.as<TernaryExpression>(), parentPrecedence);
538 break;
540 this->writeIndexExpression(expr.as<IndexExpression>());
541 break;
542 default:
543 SkDEBUGFAILF("unsupported expression: %s", expr.description().c_str());
544 break;
545 }
546}
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 2968 of file SkSLMetalCodeGenerator.cpp.

2968 {
2969 if (fProgram.fConfig->fSettings.fOptimize && !Analysis::HasSideEffects(*s.expression())) {
2970 // Don't emit dead expressions.
2971 return;
2972 }
2973 this->writeExpression(*s.expression(), Precedence::kStatement);
2974 this->write(";");
2975}
bool HasSideEffects(const Expression &expr)

◆ writeExtension()

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

Definition at line 422 of file SkSLMetalCodeGenerator.cpp.

422 {
423 this->writeLine("#extension " + std::string(ext.name()) + " : enable");
424}

◆ writeFieldAccess()

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

Definition at line 1882 of file SkSLMetalCodeGenerator.cpp.

1882 {
1883 const Field* field = &f.base()->type().fields()[f.fieldIndex()];
1884 if (FieldAccess::OwnerKind::kDefault == f.ownerKind()) {
1885 this->writeExpression(*f.base(), Precedence::kPostfix);
1886 this->write(".");
1887 }
1888 switch (field->fLayout.fBuiltin) {
1890 this->write("_out.sk_Position");
1891 break;
1893 this->write("_out.sk_PointSize");
1894 break;
1895 default:
1896 if (FieldAccess::OwnerKind::kAnonymousInterfaceBlock == f.ownerKind()) {
1897 this->write("_globals.");
1898 this->write(fInterfaceBlockNameMap[&f.base()->type()]);
1899 this->write("->");
1900 }
1901 this->writeName(field->fName);
1902 }
1903}
constexpr int SK_POSITION_BUILTIN
Definition: SkSLCompiler.h:37
constexpr int SK_POINTSIZE_BUILTIN
Definition: SkSLCompiler.h:38

◆ writeFields()

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

Definition at line 2773 of file SkSLMetalCodeGenerator.cpp.

2773 {
2774 MemoryLayout memoryLayout(MemoryLayout::Standard::kMetal);
2775 int currentOffset = 0;
2776 for (const Field& field : fields) {
2777 int fieldOffset = field.fLayout.fOffset;
2778 const Type* fieldType = field.fType;
2779 if (!memoryLayout.isSupported(*fieldType)) {
2780 fContext.fErrors->error(parentPos, "type '" + std::string(fieldType->name()) +
2781 "' is not permitted here");
2782 return;
2783 }
2784 if (fieldOffset != -1) {
2785 if (currentOffset > fieldOffset) {
2786 fContext.fErrors->error(field.fPosition,
2787 "offset of field '" + std::string(field.fName) +
2788 "' must be at least " + std::to_string(currentOffset));
2789 return;
2790 } else if (currentOffset < fieldOffset) {
2791 this->write("char pad");
2793 this->write("[");
2794 this->write(std::to_string(fieldOffset - currentOffset));
2795 this->writeLine("];");
2796 currentOffset = fieldOffset;
2797 }
2798 int alignment = memoryLayout.alignment(*fieldType);
2799 if (fieldOffset % alignment) {
2800 fContext.fErrors->error(field.fPosition,
2801 "offset of field '" + std::string(field.fName) +
2802 "' must be a multiple of " + std::to_string(alignment));
2803 return;
2804 }
2805 }
2806 if (fieldType->isUnsizedArray()) {
2807 // An unsized array always appears as the last member of a storage block. We declare
2808 // it as a one-element array and allow dereferencing past the capacity.
2809 // TODO(armansito): This is because C++ does not support flexible array members like C99
2810 // does. This generally works but it can lead to UB as compilers are free to insert
2811 // padding past the first element of the array. An alternative approach is to declare
2812 // the struct without the unsized array member and replace variable references with a
2813 // buffer offset calculation based on sizeof().
2814 this->writeModifiers(field.fModifierFlags);
2815 this->writeType(fieldType->componentType());
2816 this->write(" ");
2817 this->writeName(field.fName);
2818 this->write("[1]");
2819 } else {
2820 size_t fieldSize = memoryLayout.size(*fieldType);
2821 if (fieldSize > static_cast<size_t>(std::numeric_limits<int>::max() - currentOffset)) {
2822 fContext.fErrors->error(parentPos, "field offset overflow");
2823 return;
2824 }
2825 currentOffset += fieldSize;
2826 this->writeModifiers(field.fModifierFlags);
2827 this->writeType(*fieldType);
2828 this->write(" ");
2829 this->writeName(field.fName);
2830 }
2831 this->writeLine(";");
2832 }
2833}
int alignment(const Type *type, bool isPacked) const
void writeModifiers(ModifierFlags flags)
static float max(float r, float g, float b)
Definition: hsl.cpp:49

◆ writeForStatement()

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

Definition at line 2933 of file SkSLMetalCodeGenerator.cpp.

2933 {
2934 // Emit loops of the form 'for(;test;)' as 'while(test)', which is probably how they started
2935 if (!f.initializer() && f.test() && !f.next()) {
2936 this->write("while (");
2937 this->writeExpression(*f.test(), Precedence::kExpression);
2938 this->write(") ");
2939 this->writeStatement(*f.statement());
2940 return;
2941 }
2942
2943 this->write("for (");
2944 if (f.initializer() && !f.initializer()->isEmpty()) {
2945 this->writeStatement(*f.initializer());
2946 } else {
2947 this->write("; ");
2948 }
2949 if (f.test()) {
2950 this->writeExpression(*f.test(), Precedence::kExpression);
2951 }
2952 this->write("; ");
2953 if (f.next()) {
2954 this->writeExpression(*f.next(), Precedence::kExpression);
2955 }
2956 this->write(") ");
2957 this->writeStatement(*f.statement());
2958}

◆ writeFragCoord()

void SkSL::MetalCodeGenerator::writeFragCoord ( )
protected

Definition at line 1701 of file SkSLMetalCodeGenerator.cpp.

1701 {
1702 if (!fRTFlipName.empty()) {
1703 this->write("float4(_fragCoord.x, ");
1704 this->write(fRTFlipName.c_str());
1705 this->write(".x + ");
1706 this->write(fRTFlipName.c_str());
1707 this->write(".y * _fragCoord.y, 0.0, _fragCoord.w)");
1708 } else {
1709 this->write("float4(_fragCoord.x, _fragCoord.y, 0.0, _fragCoord.w)");
1710 }
1711}

◆ writeFunction()

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

Definition at line 2678 of file SkSLMetalCodeGenerator.cpp.

2678 {
2679 SkASSERT(!fProgram.fConfig->fSettings.fFragColorIsInOut);
2680
2681 if (!this->writeFunctionDeclaration(f.declaration())) {
2682 return;
2683 }
2684
2685 fCurrentFunction = &f.declaration();
2686 SkScopeExit clearCurrentFunction([&] { fCurrentFunction = nullptr; });
2687
2688 this->writeLine(" {");
2689
2690 if (f.declaration().isMain()) {
2691 fIndentation++;
2692 this->writeGlobalInit();
2694 this->writeThreadgroupInit();
2695 this->writeComputeMainInputs();
2696 }
2697 else {
2698 this->writeLine("Outputs _out;");
2699 this->writeLine("(void)_out;");
2700 }
2701 fIndentation--;
2702 }
2703
2704 fFunctionHeader.clear();
2705 StringStream buffer;
2706 {
2707 AutoOutputStream outputToBuffer(this, &buffer);
2708 fIndentation++;
2709 for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) {
2710 if (!stmt->isEmpty()) {
2711 this->writeStatement(*stmt);
2712 this->finishLine();
2713 }
2714 }
2715 if (f.declaration().isMain()) {
2716 // If the main function doesn't end with a return, we need to synthesize one here.
2717 if (!is_block_ending_with_return(f.body().get())) {
2719 this->finishLine();
2720 }
2721 }
2722 fIndentation--;
2723 this->writeLine("}");
2724 }
2725 this->write(fFunctionHeader);
2726 this->write(buffer.str());
2727}
const FunctionDeclaration * fCurrentFunction
static bool is_block_ending_with_return(const Statement *stmt)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126

◆ writeFunctionCall()

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

Definition at line 597 of file SkSLMetalCodeGenerator.cpp.

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

2465 {
2467 ? "_globals._anonInterface0->" SKSL_RTFLIP_NAME
2468 : "";
2469 const char* separator = "";
2470 if (f.isMain()) {
2472 this->write("fragment Outputs fragmentMain(");
2473 } else if (ProgramConfig::IsVertex(fProgram.fConfig->fKind)) {
2474 this->write("vertex Outputs vertexMain(");
2475 } else if (ProgramConfig::IsCompute(fProgram.fConfig->fKind)) {
2476 this->write("kernel void computeMain(");
2477 } else {
2478 fContext.fErrors->error(Position(), "unsupported kind of program");
2479 return false;
2480 }
2482 this->write("Inputs _in [[stage_in]]");
2483 separator = ", ";
2484 }
2485 if (-1 != fUniformBuffer) {
2486 this->write(separator);
2487 this->write("constant Uniforms& _uniforms [[buffer(" +
2489 separator = ", ";
2490 }
2491 for (const ProgramElement* e : fProgram.elements()) {
2492 if (e->is<GlobalVarDeclaration>()) {
2493 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
2494 const VarDeclaration& decl = decls.varDeclaration();
2495 const Variable* var = decl.var();
2496 const SkSL::Type::TypeKind varKind = var->type().typeKind();
2497
2498 if (varKind == Type::TypeKind::kSampler || varKind == Type::TypeKind::kTexture) {
2499 if (var->type().dimensions() != SpvDim2D) {
2500 // Not yet implemented--Skia currently only uses 2D textures.
2501 fContext.fErrors->error(decls.fPosition, "Unsupported texture dimensions");
2502 return false;
2503 }
2504
2505 int binding = getUniformBinding(var->layout());
2506 this->write(separator);
2507 separator = ", ";
2508
2509 if (varKind == Type::TypeKind::kSampler) {
2510 this->writeType(var->type().textureType());
2511 this->write(" ");
2512 this->writeName(var->mangledName());
2513 this->write(kTextureSuffix);
2514 this->write(" [[texture(");
2515 this->write(std::to_string(binding));
2516 this->write(")]], sampler ");
2517 this->writeName(var->mangledName());
2518 this->write(kSamplerSuffix);
2519 this->write(" [[sampler(");
2520 this->write(std::to_string(binding));
2521 this->write(")]]");
2522 } else {
2524 this->writeType(var->type());
2525 this->write(" ");
2526 this->writeName(var->mangledName());
2527 this->write(" [[texture(");
2528 this->write(std::to_string(binding));
2529 this->write(")]]");
2530 }
2531 } else if (ProgramConfig::IsCompute(fProgram.fConfig->fKind)) {
2532 std::string_view attr;
2533 switch (var->layout().fBuiltin) {
2535 attr = " [[threadgroups_per_grid]]";
2536 break;
2538 attr = " [[threadgroup_position_in_grid]]";
2539 break;
2541 attr = " [[thread_position_in_threadgroup]]";
2542 break;
2544 attr = " [[thread_position_in_grid]]";
2545 break;
2547 attr = " [[thread_index_in_threadgroup]]";
2548 break;
2549 default:
2550 break;
2551 }
2552 if (!attr.empty()) {
2553 this->write(separator);
2554 this->writeType(var->type());
2555 this->write(" ");
2556 this->write(var->name());
2557 this->write(attr);
2558 separator = ", ";
2559 }
2560 }
2561 } else if (e->is<InterfaceBlock>()) {
2562 const InterfaceBlock& intf = e->as<InterfaceBlock>();
2563 if (intf.typeName() == "sk_PerVertex") {
2564 continue;
2565 }
2566 this->write(separator);
2567 if (is_readonly(intf)) {
2568 this->write("const ");
2569 }
2570 this->write(is_buffer(intf) ? "device " : "constant ");
2571 this->writeType(intf.var()->type());
2572 this->write("& " );
2573 this->write(fInterfaceBlockNameMap[&intf.var()->type()]);
2574 this->write(" [[buffer(");
2575 this->write(std::to_string(this->getUniformBinding(intf.var()->layout())));
2576 this->write(")]]");
2577 separator = ", ";
2578 }
2579 }
2582 fInterfaceBlockNameMap.empty()) {
2583 this->write(separator);
2584 this->write("constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]]");
2585 fRTFlipName = "_anonInterface0." SKSL_RTFLIP_NAME;
2586 separator = ", ";
2587 }
2588 this->write(separator);
2589 this->write("bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]");
2591 this->write(", uint sk_SampleMaskIn [[sample_mask]]");
2592 }
2594 this->write(", half4 " + std::string(fCaps.fFBFetchColorName) +
2595 " [[color(0)]]\n");
2596 }
2597 separator = ", ";
2598 } else if (ProgramConfig::IsVertex(fProgram.fConfig->fKind)) {
2599 this->write(separator);
2600 this->write("uint sk_VertexID [[vertex_id]], uint sk_InstanceID [[instance_id]]");
2601 separator = ", ";
2602 }
2603 } else {
2604 this->writeType(f.returnType());
2605 this->write(" ");
2606 this->writeName(f.mangledName());
2607 this->write("(");
2608 this->writeFunctionRequirementParams(f, separator);
2609 }
2610 for (const Variable* param : f.parameters()) {
2611 // This is a workaround for our test files. They use the runtime effect signature, so main
2612 // takes a coords parameter. We detect these at IR generation time, and we omit them from
2613 // the declaration here, so the function is valid Metal. (Well, valid as long as the
2614 // coordinates aren't actually referenced.)
2615 if (f.isMain() && param == f.getMainCoordsParameter()) {
2616 continue;
2617 }
2618 this->write(separator);
2619 separator = ", ";
2620 this->writeModifiers(param->modifierFlags());
2621 this->writeType(param->type());
2622 if (pass_by_reference(param->type(), param->modifierFlags())) {
2623 this->write("&");
2624 }
2625 this->write(" ");
2626 this->writeName(param->mangledName());
2627 }
2628 this->write(")");
2629 return true;
2630}
constexpr int SK_WORKGROUPID_BUILTIN
Definition: SkSLCompiler.h:41
constexpr int SK_GLOBALINVOCATIONID_BUILTIN
Definition: SkSLCompiler.h:43
constexpr int SK_LOCALINVOCATIONID_BUILTIN
Definition: SkSLCompiler.h:42
constexpr int SK_NUMWORKGROUPS_BUILTIN
Definition: SkSLCompiler.h:40
constexpr int SK_LOCALINVOCATIONINDEX_BUILTIN
Definition: SkSLCompiler.h:44
#define SKSL_RTFLIP_NAME
Definition: SkSLProgram.h:19
static constexpr char kSamplerSuffix[]
static constexpr char kTextureSuffix[]
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
Definition: SkSLProgram.h:165
const char * fFBFetchColorName
Definition: SkSLUtil.h:158

◆ writeFunctionPrototype()

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

Definition at line 2632 of file SkSLMetalCodeGenerator.cpp.

2632 {
2633 this->writeFunctionDeclaration(f.declaration());
2634 this->writeLine(";");
2635}

◆ writeFunctionRequirementArgs()

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

Definition at line 2355 of file SkSLMetalCodeGenerator.cpp.

2356 {
2358 if (requirements & kInputs_Requirement) {
2359 this->write(separator);
2360 this->write("_in");
2361 separator = ", ";
2362 }
2364 this->write(separator);
2365 this->write("_out");
2366 separator = ", ";
2367 }
2369 this->write(separator);
2370 this->write("_uniforms");
2371 separator = ", ";
2372 }
2374 this->write(separator);
2375 this->write("_globals");
2376 separator = ", ";
2377 }
2379 this->write(separator);
2380 this->write("_fragCoord");
2381 separator = ", ";
2382 }
2384 this->write(separator);
2385 this->write("sk_SampleMaskIn");
2386 separator = ", ";
2387 }
2389 this->write(separator);
2390 this->write("sk_VertexID");
2391 separator = ", ";
2392 }
2394 this->write(separator);
2395 this->write("sk_InstanceID");
2396 separator = ", ";
2397 }
2399 this->write(separator);
2400 this->write("_threadgroups");
2401 separator = ", ";
2402 }
2403}

◆ writeFunctionRequirementParams()

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

Definition at line 2405 of file SkSLMetalCodeGenerator.cpp.

2406 {
2408 if (requirements & kInputs_Requirement) {
2409 this->write(separator);
2410 this->write("Inputs _in");
2411 separator = ", ";
2412 }
2414 this->write(separator);
2415 this->write("thread Outputs& _out");
2416 separator = ", ";
2417 }
2419 this->write(separator);
2420 this->write("Uniforms _uniforms");
2421 separator = ", ";
2422 }
2424 this->write(separator);
2425 this->write("thread Globals& _globals");
2426 separator = ", ";
2427 }
2429 this->write(separator);
2430 this->write("float4 _fragCoord");
2431 separator = ", ";
2432 }
2434 this->write(separator);
2435 this->write("uint sk_SampleMaskIn");
2436 separator = ", ";
2437 }
2439 this->write(separator);
2440 this->write("uint sk_VertexID");
2441 separator = ", ";
2442 }
2444 this->write(separator);
2445 this->write("uint sk_InstanceID");
2446 separator = ", ";
2447 }
2449 this->write(separator);
2450 this->write("threadgroup Threadgroups& _threadgroups");
2451 separator = ", ";
2452 }
2453}

◆ writeGlobalInit()

void SkSL::MetalCodeGenerator::writeGlobalInit ( )
protected

Definition at line 3353 of file SkSLMetalCodeGenerator.cpp.

3353 {
3354 class : public GlobalStructVisitor {
3355 public:
3356 void visitInterfaceBlock(const InterfaceBlock& blockType,
3357 std::string_view blockName) override {
3358 this->addElement();
3359 fCodeGen->write("&");
3360 fCodeGen->writeName(blockName);
3361 }
3362 void visitTexture(const Type&, std::string_view name) override {
3363 this->addElement();
3364 fCodeGen->writeName(name);
3365 }
3366 void visitSampler(const Type&, std::string_view name) override {
3367 this->addElement();
3368 fCodeGen->write("{");
3369 fCodeGen->writeName(name);
3370 fCodeGen->write(kTextureSuffix);
3371 fCodeGen->write(", ");
3372 fCodeGen->writeName(name);
3373 fCodeGen->write(kSamplerSuffix);
3374 fCodeGen->write("}");
3375 }
3376 void visitConstantVariable(const VarDeclaration& decl) override {
3377 // Constant-expression variables aren't put in the global struct.
3378 }
3379 void visitNonconstantVariable(const Variable& var, const Expression* value) override {
3380 this->addElement();
3381 if (value) {
3382 fCodeGen->writeVarInitializer(var, *value);
3383 } else {
3384 fCodeGen->write("{}");
3385 }
3386 }
3387 void addElement() {
3388 if (fFirst) {
3389 fCodeGen->write("Globals _globals{");
3390 fFirst = false;
3391 } else {
3392 fCodeGen->write(", ");
3393 }
3394 }
3395 void finish() {
3396 if (!fFirst) {
3397 fCodeGen->writeLine("};");
3398 fCodeGen->writeLine("(void)_globals;");
3399 }
3400 }
3401 MetalCodeGenerator* fCodeGen = nullptr;
3402 bool fFirst = true;
3403 } visitor;
3404
3405 visitor.fCodeGen = this;
3406 this->visitGlobalStruct(&visitor);
3407 visitor.finish();
3408}
uint8_t value

◆ writeGlobalStruct()

void SkSL::MetalCodeGenerator::writeGlobalStruct ( )
protected

Definition at line 3289 of file SkSLMetalCodeGenerator.cpp.

3289 {
3290 class : public GlobalStructVisitor {
3291 public:
3292 void visitInterfaceBlock(const InterfaceBlock& block,
3293 std::string_view blockName) override {
3294 this->addElement();
3295 fCodeGen->write(" ");
3296 if (is_readonly(block)) {
3297 fCodeGen->write("const ");
3298 }
3299 fCodeGen->write(is_buffer(block) ? "device " : "constant ");
3300 fCodeGen->write(block.typeName());
3301 fCodeGen->write("* ");
3302 fCodeGen->writeName(blockName);
3303 fCodeGen->write(";\n");
3304 }
3305 void visitTexture(const Type& type, std::string_view name) override {
3306 this->addElement();
3307 fCodeGen->write(" ");
3308 fCodeGen->writeType(type);
3309 fCodeGen->write(" ");
3310 fCodeGen->writeName(name);
3311 fCodeGen->write(";\n");
3312 }
3313 void visitSampler(const Type&, std::string_view name) override {
3314 this->addElement();
3315 fCodeGen->write(" sampler2D ");
3316 fCodeGen->writeName(name);
3317 fCodeGen->write(";\n");
3318 }
3319 void visitConstantVariable(const VarDeclaration& decl) override {
3320 // Constants aren't added to the global struct.
3321 }
3322 void visitNonconstantVariable(const Variable& var, const Expression* value) override {
3323 this->addElement();
3324 fCodeGen->write(" ");
3325 fCodeGen->writeModifiers(var.modifierFlags());
3326 fCodeGen->writeType(var.type());
3327 fCodeGen->write(" ");
3328 fCodeGen->writeName(var.mangledName());
3329 fCodeGen->write(";\n");
3330 }
3331 void addElement() {
3332 if (fFirst) {
3333 fCodeGen->write("struct Globals {\n");
3334 fFirst = false;
3335 }
3336 }
3337 void finish() {
3338 if (!fFirst) {
3339 fCodeGen->writeLine("};");
3340 fFirst = true;
3341 }
3342 }
3343
3344 MetalCodeGenerator* fCodeGen = nullptr;
3345 bool fFirst = true;
3346 } visitor;
3347
3348 visitor.fCodeGen = this;
3349 this->visitGlobalStruct(&visitor);
3350 visitor.finish();
3351}

◆ writeHeader()

void SkSL::MetalCodeGenerator::writeHeader ( )
protected

Definition at line 3039 of file SkSLMetalCodeGenerator.cpp.

3039 {
3040 this->writeLine("#include <metal_stdlib>");
3041 this->writeLine("#include <simd/simd.h>");
3042 this->writeLine("#ifdef __clang__");
3043 this->writeLine("#pragma clang diagnostic ignored \"-Wall\"");
3044 this->writeLine("#endif");
3045 this->writeLine("using namespace metal;");
3046}

◆ writeIfStatement()

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

Definition at line 2922 of file SkSLMetalCodeGenerator.cpp.

2922 {
2923 this->write("if (");
2924 this->writeExpression(*stmt.test(), Precedence::kExpression);
2925 this->write(") ");
2926 this->writeStatement(*stmt.ifTrue());
2927 if (stmt.ifFalse()) {
2928 this->write(" else ");
2929 this->writeStatement(*stmt.ifFalse());
2930 }
2931}

◆ writeIndexExpression()

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

Definition at line 1858 of file SkSLMetalCodeGenerator.cpp.

1858 {
1859 // Metal does not seem to handle assignment into `vec.zyx[i]` properly--it compiles, but the
1860 // results are wrong. We rewrite the expression as `vec[uint3(2,1,0)[i]]` instead. (Filed with
1861 // Apple as FB12055941.)
1862 if (expr.base()->is<Swizzle>() && expr.base()->as<Swizzle>().components().size() > 1) {
1863 const Swizzle& swizzle = expr.base()->as<Swizzle>();
1864 this->writeExpression(*swizzle.base(), Precedence::kPostfix);
1865 this->write("[uint" + std::to_string(swizzle.components().size()) + "(");
1866 auto separator = SkSL::String::Separator();
1867 for (int8_t component : swizzle.components()) {
1868 this->write(separator());
1869 this->write(std::to_string(component));
1870 }
1871 this->write(")[");
1872 this->writeIndexInnerExpression(*expr.index());
1873 this->write("]]");
1874 } else {
1875 this->writeExpression(*expr.base(), Precedence::kPostfix);
1876 this->write("[");
1877 this->writeIndexInnerExpression(*expr.index());
1878 this->write("]");
1879 }
1880}
void writeIndexInnerExpression(const Expression &expr)

◆ writeIndexInnerExpression()

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

Definition at line 1826 of file SkSLMetalCodeGenerator.cpp.

1826 {
1828 // If this expression already exists in the index-substitution map, use the substitute.
1829 if (const std::string* existing = fIndexSubstitutionData->fMap.find(&expr)) {
1830 this->write(*existing);
1831 return;
1832 }
1833
1834 // If this expression is non-trivial, we will need to create a scratch variable and store
1835 // its value there.
1836 if (fIndexSubstitutionData->fCreateSubstitutes && !Analysis::IsTrivialExpression(expr)) {
1837 // Create a substitute variable and emit it into the main stream.
1838 std::string scratchVar = this->getTempVariable(expr.type());
1839 this->write(scratchVar);
1840
1841 // Initialize the substitute variable in the prefix-stream.
1842 AutoOutputStream outputToPrefixStream(this, &fIndexSubstitutionData->fPrefixStream);
1843 this->write(scratchVar);
1844 this->write(" = ");
1845 this->writeExpression(expr, Precedence::kAssignment);
1846 this->write(", ");
1847
1848 // Remember the substitute variable in our map.
1849 fIndexSubstitutionData->fMap.set(&expr, std::move(scratchVar));
1850 return;
1851 }
1852 }
1853
1854 // We don't require index-substitution; just emit the expression normally.
1855 this->writeExpression(expr, Precedence::kExpression);
1856}
bool IsTrivialExpression(const Expression &expr)

◆ writeInputStruct()

void SkSL::MetalCodeGenerator::writeInputStruct ( )
protected

Definition at line 3120 of file SkSLMetalCodeGenerator.cpp.

3120 {
3121 this->write("struct Inputs {\n");
3122 for (const ProgramElement* e : fProgram.elements()) {
3123 if (e->is<GlobalVarDeclaration>()) {
3124 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
3125 const Variable& var = *decls.varDeclaration().var();
3126 if (is_input(var)) {
3127 this->write(" ");
3129 needs_address_space(var.type(), var.modifierFlags())) {
3130 // TODO: address space support
3131 this->write("device ");
3132 }
3133 this->writeType(var.type());
3134 if (pass_by_reference(var.type(), var.modifierFlags())) {
3135 this->write("&");
3136 }
3137 this->write(" ");
3138 this->writeName(var.mangledName());
3139 if (-1 != var.layout().fLocation) {
3141 this->write(" [[attribute(" + std::to_string(var.layout().fLocation) +
3142 ")]]");
3143 } else if (ProgramConfig::IsFragment(fProgram.fConfig->fKind)) {
3144 this->write(" [[user(locn" + std::to_string(var.layout().fLocation) +
3145 ")]]");
3146 }
3147 }
3148 this->write(";\n");
3149 }
3150 }
3151 }
3152 this->write("};\n");
3153}
static bool needs_address_space(const Type &type, ModifierFlags modifiers)

◆ writeInterfaceBlock()

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

Definition at line 2741 of file SkSLMetalCodeGenerator.cpp.

2741 {
2742 if (intf.typeName() == "sk_PerVertex") {
2743 return;
2744 }
2745 const Type* structType = &intf.var()->type().componentType();
2746 this->writeModifiers(intf.var()->modifierFlags());
2747 this->write("struct ");
2748 this->writeType(*structType);
2749 this->writeLine(" {");
2750 fIndentation++;
2751 this->writeFields(structType->fields(), structType->fPosition);
2753 this->writeLine("float2 " SKSL_RTFLIP_NAME ";");
2754 }
2755 fIndentation--;
2756 this->write("}");
2757 if (!intf.instanceName().empty()) {
2758 this->write(" ");
2759 this->write(intf.instanceName());
2760 if (intf.arraySize() > 0) {
2761 this->write("[");
2762 this->write(std::to_string(intf.arraySize()));
2763 this->write("]");
2764 }
2765 fInterfaceBlockNameMap.set(&intf.var()->type(), std::string(intf.instanceName()));
2766 } else {
2767 fInterfaceBlockNameMap.set(&intf.var()->type(),
2768 "_anonInterface" + std::to_string(fAnonInterfaceCount++));
2769 }
2770 this->writeLine(";");
2771}
void writeFields(SkSpan< const Field > fields, Position pos)

◆ writeInterfaceBlocks()

void SkSL::MetalCodeGenerator::writeInterfaceBlocks ( )
protected

Definition at line 3213 of file SkSLMetalCodeGenerator.cpp.

3213 {
3214 bool wroteInterfaceBlock = false;
3215 for (const ProgramElement* e : fProgram.elements()) {
3216 if (e->is<InterfaceBlock>()) {
3217 this->writeInterfaceBlock(e->as<InterfaceBlock>());
3218 wroteInterfaceBlock = true;
3219 }
3220 }
3221 if (!wroteInterfaceBlock &&
3223 this->writeLine("struct sksl_synthetic_uniforms {");
3224 this->writeLine(" float2 " SKSL_RTFLIP_NAME ";");
3225 this->writeLine("};");
3226 }
3227}
void writeInterfaceBlock(const InterfaceBlock &intf)

◆ writeIntrinsicCall()

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

Definition at line 879 of file SkSLMetalCodeGenerator.cpp.

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

◆ 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 410 of file SkSLMetalCodeGenerator.cpp.

410 {
411 this->write(s);
413 fAtLineStart = true;
414}

◆ writeLiteral()

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

Definition at line 2330 of file SkSLMetalCodeGenerator.cpp.

2330 {
2331 const Type& type = l.type();
2332 if (type.isFloat()) {
2333 this->write(l.description(OperatorPrecedence::kExpression));
2334 if (!l.type().highPrecision()) {
2335 this->write("h");
2336 }
2337 return;
2338 }
2339 if (type.isInteger()) {
2340 if (type.matches(*fContext.fTypes.fUInt)) {
2341 this->write(std::to_string(l.intValue() & 0xffffffff));
2342 this->write("u");
2343 } else if (type.matches(*fContext.fTypes.fUShort)) {
2344 this->write(std::to_string(l.intValue() & 0xffff));
2345 this->write("u");
2346 } else {
2347 this->write(std::to_string(l.intValue()));
2348 }
2349 return;
2350 }
2351 SkASSERT(type.isBoolean());
2352 this->write(l.description(OperatorPrecedence::kExpression));
2353}
const std::unique_ptr< Type > fUShort
const std::unique_ptr< Type > fUInt

◆ writeMatrixCompMult()

void SkSL::MetalCodeGenerator::writeMatrixCompMult ( )
protected

Definition at line 827 of file SkSLMetalCodeGenerator.cpp.

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

◆ writeMatrixDivisionHelpers()

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

Definition at line 1970 of file SkSLMetalCodeGenerator.cpp.

1970 {
1971 SkASSERT(type.isMatrix());
1972
1973 std::string key = "Matrix / " + this->typeName(type);
1974
1975 if (!fHelpers.contains(key)) {
1976 fHelpers.add(key);
1977 std::string typeName = this->typeName(type);
1978
1980 "thread %s operator/(const %s left, const %s right) {\n"
1981 " return %s(",
1982 typeName.c_str(), typeName.c_str(), typeName.c_str(), typeName.c_str());
1983
1984 const char* separator = "";
1985 for (int index=0; index<type.columns(); ++index) {
1986 fExtraFunctions.printf("%sleft[%d] / right[%d]", separator, index, index);
1987 separator = ", ";
1988 }
1989
1990 fExtraFunctions.printf(");\n"
1991 "}\n"
1992 "thread %s& operator/=(thread %s& left, thread const %s& right) {\n"
1993 " left = left / right;\n"
1994 " return left;\n"
1995 "}\n",
1996 typeName.c_str(), typeName.c_str(), typeName.c_str());
1997 }
1998}

◆ writeMatrixEqualityHelpers()

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

Definition at line 1930 of file SkSLMetalCodeGenerator.cpp.

1930 {
1931 SkASSERT(left.isMatrix());
1932 SkASSERT(right.isMatrix());
1933 SkASSERT(left.rows() == right.rows());
1934 SkASSERT(left.columns() == right.columns());
1935
1936 std::string key = "Matrix == " + this->typeName(left) + ":" + this->typeName(right);
1937
1938 if (!fHelpers.contains(key)) {
1939 fHelpers.add(key);
1941thread bool operator==(const %s left, const %s right);
1942thread bool operator!=(const %s left, const %s right);
1943)",
1944 this->typeName(left).c_str(),
1945 this->typeName(right).c_str(),
1946 this->typeName(left).c_str(),
1947 this->typeName(right).c_str());
1948
1950 "thread bool operator==(const %s left, const %s right) {\n"
1951 " return ",
1952 this->typeName(left).c_str(), this->typeName(right).c_str());
1953
1954 const char* separator = "";
1955 for (int index=0; index<left.columns(); ++index) {
1956 fExtraFunctions.printf("%sall(left[%d] == right[%d])", separator, index, index);
1957 separator = " &&\n ";
1958 }
1959
1961 ";\n"
1962 "}\n"
1963 "thread bool operator!=(const %s left, const %s right) {\n"
1964 " return !(left == right);\n"
1965 "}\n",
1966 this->typeName(left).c_str(), this->typeName(right).c_str());
1967 }
1968}

◆ writeMatrixTimesEqualHelper()

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

Definition at line 1911 of file SkSLMetalCodeGenerator.cpp.

1912 {
1913 SkASSERT(left.isMatrix());
1914 SkASSERT(right.isMatrix());
1915 SkASSERT(result.isMatrix());
1916
1917 std::string key = "Matrix *= " + this->typeName(left) + ":" + this->typeName(right);
1918
1919 if (!fHelpers.contains(key)) {
1920 fHelpers.add(key);
1921 fExtraFunctions.printf("thread %s& operator*=(thread %s& left, thread const %s& right) {\n"
1922 " left = left * right;\n"
1923 " return left;\n"
1924 "}\n",
1925 this->typeName(result).c_str(), this->typeName(left).c_str(),
1926 this->typeName(right).c_str());
1927 }
1928}
GAsyncResult * result

◆ writeMinAbsHack()

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

◆ writeModifiers()

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

Definition at line 2729 of file SkSLMetalCodeGenerator.cpp.

2729 {
2732 this->write("device ");
2733 } else if (flags & ModifierFlag::kOut) {
2734 this->write("thread ");
2735 }
2736 if (flags.isConst()) {
2737 this->write("const ");
2738 }
2739}
FlutterSemanticsFlag flags

◆ writeName()

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

Definition at line 2839 of file SkSLMetalCodeGenerator.cpp.

2839 {
2841 this->write("_"); // adding underscore before name to avoid conflict with reserved words
2842 }
2843 this->write(name);
2844}

◆ writeNumberAsMatrix()

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

Definition at line 2119 of file SkSLMetalCodeGenerator.cpp.

2119 {
2120 SkASSERT(expr.type().isNumber());
2121 SkASSERT(matrixType.isMatrix());
2122
2123 // Componentwise multiply the scalar against a matrix of the desired size which contains all 1s.
2124 this->write("(");
2125 this->write(this->splatMatrixOf1(matrixType));
2126 this->write(" * ");
2127 this->writeExpression(expr, Precedence::kMultiplicative);
2128 this->write(")");
2129}
std::string splatMatrixOf1(const Type &type)

◆ writeOuterProduct()

void SkSL::MetalCodeGenerator::writeOuterProduct ( )
protected

Definition at line 841 of file SkSLMetalCodeGenerator.cpp.

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

◆ writeOutputStruct()

void SkSL::MetalCodeGenerator::writeOutputStruct ( )
protected

Definition at line 3155 of file SkSLMetalCodeGenerator.cpp.

3155 {
3156 this->write("struct Outputs {\n");
3158 this->write(" float4 sk_Position [[position]];\n");
3159 } else if (ProgramConfig::IsFragment(fProgram.fConfig->fKind)) {
3160 this->write(" half4 sk_FragColor [[color(0)]];\n");
3162 this->write(" half4 sk_SecondaryFragColor [[color(0), index(1)]];\n");
3163 }
3164 }
3165 for (const ProgramElement* e : fProgram.elements()) {
3166 if (e->is<GlobalVarDeclaration>()) {
3167 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
3168 const Variable& var = *decls.varDeclaration().var();
3169 if (var.layout().fBuiltin == SK_SAMPLEMASK_BUILTIN) {
3170 this->write(" uint sk_SampleMask [[sample_mask]];\n");
3171 continue;
3172 }
3173 if (is_output(var)) {
3174 this->write(" ");
3176 needs_address_space(var.type(), var.modifierFlags())) {
3177 // TODO: address space support
3178 this->write("device ");
3179 }
3180 this->writeType(var.type());
3182 pass_by_reference(var.type(), var.modifierFlags())) {
3183 this->write("&");
3184 }
3185 this->write(" ");
3186 this->writeName(var.mangledName());
3187
3188 int location = var.layout().fLocation;
3189 if (!ProgramConfig::IsCompute(fProgram.fConfig->fKind) && location < 0 &&
3190 var.type().typeKind() != Type::TypeKind::kTexture) {
3191 fContext.fErrors->error(var.fPosition,
3192 "Metal out variables must have 'layout(location=...)'");
3193 } else if (ProgramConfig::IsVertex(fProgram.fConfig->fKind)) {
3194 this->write(" [[user(locn" + std::to_string(location) + ")]]");
3195 } else if (ProgramConfig::IsFragment(fProgram.fConfig->fKind)) {
3196 this->write(" [[color(" + std::to_string(location) + ")");
3197 int colorIndex = var.layout().fIndex;
3198 if (colorIndex) {
3199 this->write(", index(" + std::to_string(colorIndex) + ")");
3200 }
3201 this->write("]]");
3202 }
3203 this->write(";\n");
3204 }
3205 }
3206 }
3208 this->write(" float sk_PointSize [[point_size]];\n");
3209 }
3210 this->write("};\n");
3211}

◆ writePostfixExpression()

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

Definition at line 2290 of file SkSLMetalCodeGenerator.cpp.

2291 {
2292 const Operator op = p.getOperator();
2293 switch (op.kind()) {
2294 case Operator::Kind::PLUSPLUS:
2295 case Operator::Kind::MINUSMINUS:
2296 if (p.operand()->type().isMatrix()) {
2297 // We need to transform `x++` or `x--` into `+=` and `-=` on a matrix.
2298 // Unfortunately, that requires making a temporary copy of the old value and
2299 // emitting a sequence expression: `((temp = mat), (mat += T(1.0, ...)), temp)`.
2300 std::string tempMatrix = this->getTempVariable(p.operand()->type());
2301 this->write("((");
2302 this->write(tempMatrix);
2303 this->write(" = ");
2304 this->writeExpression(*p.operand(), Precedence::kAssignment);
2305 this->write("), (");
2306 this->writeExpression(*p.operand(), Precedence::kAssignment);
2307 this->write(op.kind() == Operator::Kind::PLUSPLUS ? " += " : " -= ");
2308 this->write(this->splatMatrixOf1(p.operand()->type()));
2309 this->write("), ");
2310 this->write(tempMatrix);
2311 this->write(")");
2312 return;
2313 }
2314 break;
2315
2316 default:
2317 break;
2318 }
2319
2320 if (Precedence::kPostfix >= parentPrecedence) {
2321 this->write("(");
2322 }
2323 this->writeExpression(*p.operand(), Precedence::kPostfix);
2324 this->write(op.tightOperatorName());
2325 if (Precedence::kPostfix >= parentPrecedence) {
2326 this->write(")");
2327 }
2328}

◆ writePrecisionModifier()

void SkSL::MetalCodeGenerator::writePrecisionModifier ( )
protected

◆ writePrefixExpression()

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

Definition at line 2239 of file SkSLMetalCodeGenerator.cpp.

2240 {
2241 const Operator op = p.getOperator();
2242 switch (op.kind()) {
2244 // According to the MSL specification, the arithmetic unary operators (+ and –) do not
2245 // act upon matrix-typed operands. We treat the unary "+" as a no-op for all operands.
2246 this->writeExpression(*p.operand(), Precedence::kPrefix);
2247 return;
2248
2249 case Operator::Kind::MINUS:
2250 // Transform the unary `-` on a matrix type to a multiplication by -1.
2251 if (p.operand()->type().isMatrix()) {
2252 this->write(p.type().componentType().highPrecision() ? "(-1.0 * "
2253 : "(-1.0h * ");
2254 this->writeExpression(*p.operand(), Precedence::kMultiplicative);
2255 this->write(")");
2256 return;
2257 }
2258 break;
2259
2260 case Operator::Kind::PLUSPLUS:
2261 case Operator::Kind::MINUSMINUS:
2262 if (p.operand()->type().isMatrix()) {
2263 // Transform `++x` or `--x` on a matrix type to `mat += T(1.0, ...)` or
2264 // `mat -= T(1.0, ...)`.
2265 this->write("(");
2266 this->writeExpression(*p.operand(), Precedence::kAssignment);
2267 this->write(op.kind() == Operator::Kind::PLUSPLUS ? " += " : " -= ");
2268 this->write(this->splatMatrixOf1(p.operand()->type()));
2269 this->write(")");
2270 return;
2271 }
2272 break;
2273
2274 default:
2275 break;
2276 }
2277
2278 if (Precedence::kPrefix >= parentPrecedence) {
2279 this->write("(");
2280 }
2281
2282 this->write(op.tightOperatorName());
2283 this->writeExpression(*p.operand(), Precedence::kPrefix);
2284
2285 if (Precedence::kPrefix >= parentPrecedence) {
2286 this->write(")");
2287 }
2288}

◆ writeProgramElement()

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

Definition at line 3490 of file SkSLMetalCodeGenerator.cpp.

3490 {
3491 switch (e.kind()) {
3493 break;
3494 case ProgramElement::Kind::kGlobalVar:
3495 break;
3496 case ProgramElement::Kind::kInterfaceBlock:
3497 // Handled in writeInterfaceBlocks; do nothing.
3498 break;
3499 case ProgramElement::Kind::kStructDefinition:
3500 // Handled in writeStructDefinitions; do nothing.
3501 break;
3503 this->writeFunction(e.as<FunctionDefinition>());
3504 break;
3505 case ProgramElement::Kind::kFunctionPrototype:
3506 this->writeFunctionPrototype(e.as<FunctionPrototype>());
3507 break;
3508 case ProgramElement::Kind::kModifiers:
3509 // Not necessary in Metal; do nothing.
3510 break;
3511 default:
3512 SkDEBUGFAILF("unsupported program element: %s\n", e.description().c_str());
3513 break;
3514 }
3515}
void writeFunction(const FunctionDefinition &f)
void writeFunctionPrototype(const FunctionPrototype &f)

◆ writeReturnStatement()

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

Definition at line 3014 of file SkSLMetalCodeGenerator.cpp.

3014 {
3016 if (r.expression()) {
3017 if (r.expression()->type().matches(*fContext.fTypes.fHalf4)) {
3018 this->write("_out.sk_FragColor = ");
3019 this->writeExpression(*r.expression(), Precedence::kExpression);
3020 this->writeLine(";");
3021 } else {
3022 fContext.fErrors->error(r.fPosition,
3023 "Metal does not support returning '" +
3024 r.expression()->type().description() + "' from main()");
3025 }
3026 }
3028 return;
3029 }
3030
3031 this->write("return");
3032 if (r.expression()) {
3033 this->write(" ");
3034 this->writeExpression(*r.expression(), Precedence::kExpression);
3035 }
3036 this->write(";");
3037}
const std::unique_ptr< Type > fHalf4

◆ writeReturnStatementFromMain()

void SkSL::MetalCodeGenerator::writeReturnStatementFromMain ( )
protected

Definition at line 3002 of file SkSLMetalCodeGenerator.cpp.

3002 {
3003 // main functions in Metal return a magic _out parameter that doesn't exist in SkSL.
3006 this->write("return _out;");
3007 } else if (ProgramConfig::IsCompute(fProgram.fConfig->fKind)) {
3008 this->write("return;");
3009 } else {
3010 SkDEBUGFAIL("unsupported kind of program");
3011 }
3012}

◆ writeSampler2DPolyfill()

void SkSL::MetalCodeGenerator::writeSampler2DPolyfill ( )
protected

Definition at line 3048 of file SkSLMetalCodeGenerator.cpp.

3048 {
3049 class : public GlobalStructVisitor {
3050 public:
3051 void visitSampler(const Type&, std::string_view) override {
3052 if (fWrotePolyfill) {
3053 return;
3054 }
3055 fWrotePolyfill = true;
3056
3057 std::string polyfill = SkSL::String::printf(R"(
3058struct sampler2D {
3059 texture2d<half> tex;
3060 sampler smp;
3061};
3062half4 sample(sampler2D i, float2 p, float b=%g) { return i.tex.sample(i.smp, p, bias(b)); }
3063half4 sample(sampler2D i, float3 p, float b=%g) { return i.tex.sample(i.smp, p.xy / p.z, bias(b)); }
3064half4 sampleLod(sampler2D i, float2 p, float lod) { return i.tex.sample(i.smp, p, level(lod)); }
3065half4 sampleLod(sampler2D i, float3 p, float lod) {
3066 return i.tex.sample(i.smp, p.xy / p.z, level(lod));
3067}
3068half4 sampleGrad(sampler2D i, float2 p, float2 dPdx, float2 dPdy) {
3069 return i.tex.sample(i.smp, p, gradient2d(dPdx, dPdy));
3070}
3071
3072)",
3073 fTextureBias,
3074 fTextureBias);
3075 fCodeGen->write(polyfill.c_str());
3076 }
3077
3078 MetalCodeGenerator* fCodeGen = nullptr;
3079 float fTextureBias = 0.0f;
3080 bool fWrotePolyfill = false;
3081 } visitor;
3082
3083 visitor.fCodeGen = this;
3084 visitor.fTextureBias = fProgram.fConfig->fSettings.fSharpenTextures ? kSharpenTexturesBias
3085 : 0.0f;
3086 this->visitGlobalStruct(&visitor);
3087}
static constexpr float kSharpenTexturesBias

◆ writeSimpleIntrinsic()

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

Definition at line 862 of file SkSLMetalCodeGenerator.cpp.

862 {
863 // Write out an intrinsic function call exactly as-is. No muss no fuss.
864 this->write(c.function().name());
865 this->writeArgumentList(c.arguments());
866}

◆ writeStatement()

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

Definition at line 2858 of file SkSLMetalCodeGenerator.cpp.

2858 {
2859 switch (s.kind()) {
2860 case Statement::Kind::kBlock:
2861 this->writeBlock(s.as<Block>());
2862 break;
2863 case Statement::Kind::kExpression:
2864 this->writeExpressionStatement(s.as<ExpressionStatement>());
2865 break;
2866 case Statement::Kind::kReturn:
2867 this->writeReturnStatement(s.as<ReturnStatement>());
2868 break;
2869 case Statement::Kind::kVarDeclaration:
2870 this->writeVarDeclaration(s.as<VarDeclaration>());
2871 break;
2872 case Statement::Kind::kIf:
2873 this->writeIfStatement(s.as<IfStatement>());
2874 break;
2875 case Statement::Kind::kFor:
2876 this->writeForStatement(s.as<ForStatement>());
2877 break;
2878 case Statement::Kind::kDo:
2879 this->writeDoStatement(s.as<DoStatement>());
2880 break;
2881 case Statement::Kind::kSwitch:
2882 this->writeSwitchStatement(s.as<SwitchStatement>());
2883 break;
2884 case Statement::Kind::kBreak:
2885 this->write("break;");
2886 break;
2888 this->write("continue;");
2889 break;
2891 this->write("discard_fragment();");
2892 break;
2894 this->write(";");
2895 break;
2896 default:
2897 SkDEBUGFAILF("unsupported statement: %s", s.description().c_str());
2898 break;
2899 }
2900}
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 469 of file SkSLMetalCodeGenerator.cpp.

469 {
470 const Type& type = s.type();
471 this->writeLine("struct " + type.displayName() + " {");
472 fIndentation++;
473 this->writeFields(type.fields(), type.fPosition);
474 fIndentation--;
475 this->writeLine("};");
476}

◆ writeStructDefinitions()

void SkSL::MetalCodeGenerator::writeStructDefinitions ( )
protected

Definition at line 3229 of file SkSLMetalCodeGenerator.cpp.

3229 {
3230 for (const ProgramElement* e : fProgram.elements()) {
3231 if (e->is<StructDefinition>()) {
3232 this->writeStructDefinition(e->as<StructDefinition>());
3233 }
3234 }
3235}
void writeStructDefinition(const StructDefinition &s)

◆ writeStructEqualityHelpers()

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

Definition at line 2037 of file SkSLMetalCodeGenerator.cpp.

2037 {
2038 SkASSERT(type.isStruct());
2039 std::string key = "StructEquality " + this->typeName(type);
2040
2041 if (!fHelpers.contains(key)) {
2042 fHelpers.add(key);
2043 // If one of the struct's fields needs a helper as well, we need to emit that one first.
2044 for (const Field& field : type.fields()) {
2045 this->writeEqualityHelpers(*field.fType, *field.fType);
2046 }
2047
2048 // Write operator== and operator!= for this struct, since those are assumed to exist in SkSL
2049 // and GLSL but do not exist by default in Metal.
2051thread bool operator==(thread const %s& left, thread const %s& right);
2052thread bool operator!=(thread const %s& left, thread const %s& right);
2053)",
2054 this->typeName(type).c_str(),
2055 this->typeName(type).c_str(),
2056 this->typeName(type).c_str(),
2057 this->typeName(type).c_str());
2058
2060 "thread bool operator==(thread const %s& left, thread const %s& right) {\n"
2061 " return ",
2062 this->typeName(type).c_str(),
2063 this->typeName(type).c_str());
2064
2065 const char* separator = "";
2066 for (const Field& field : type.fields()) {
2067 if (field.fType->isArray()) {
2069 "%s(make_array_ref(left.%.*s) == make_array_ref(right.%.*s))",
2070 separator,
2071 (int)field.fName.size(), field.fName.data(),
2072 (int)field.fName.size(), field.fName.data());
2073 } else {
2074 fExtraFunctions.printf("%sall(left.%.*s == right.%.*s)",
2075 separator,
2076 (int)field.fName.size(), field.fName.data(),
2077 (int)field.fName.size(), field.fName.data());
2078 }
2079 separator = " &&\n ";
2080 }
2082 ";\n"
2083 "}\n"
2084 "thread bool operator!=(thread const %s& left, thread const %s& right) {\n"
2085 " return !(left == right);\n"
2086 "}\n",
2087 this->typeName(type).c_str(),
2088 this->typeName(type).c_str());
2089 }
2090}

◆ writeSwitchStatement()

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

Definition at line 2977 of file SkSLMetalCodeGenerator.cpp.

2977 {
2978 this->write("switch (");
2979 this->writeExpression(*s.value(), Precedence::kExpression);
2980 this->writeLine(") {");
2981 fIndentation++;
2982 for (const std::unique_ptr<Statement>& stmt : s.cases()) {
2983 const SwitchCase& c = stmt->as<SwitchCase>();
2984 if (c.isDefault()) {
2985 this->writeLine("default:");
2986 } else {
2987 this->write("case ");
2988 this->write(std::to_string(c.value()));
2989 this->writeLine(":");
2990 }
2991 if (!c.statement()->isEmpty()) {
2992 fIndentation++;
2993 this->writeStatement(*c.statement());
2994 this->finishLine();
2995 fIndentation--;
2996 }
2997 }
2998 fIndentation--;
2999 this->write("}");
3000}

◆ writeSwizzle()

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

Definition at line 1905 of file SkSLMetalCodeGenerator.cpp.

1905 {
1906 this->writeExpression(*swizzle.base(), Precedence::kPostfix);
1907 this->write(".");
1908 this->write(Swizzle::MaskString(swizzle.components()));
1909}
static std::string MaskString(const ComponentArray &inComponents)

◆ writeTernaryExpression()

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

Definition at line 2224 of file SkSLMetalCodeGenerator.cpp.

2225 {
2226 if (Precedence::kTernary >= parentPrecedence) {
2227 this->write("(");
2228 }
2229 this->writeExpression(*t.test(), Precedence::kTernary);
2230 this->write(" ? ");
2231 this->writeExpression(*t.ifTrue(), Precedence::kTernary);
2232 this->write(" : ");
2233 this->writeExpression(*t.ifFalse(), Precedence::kTernary);
2234 if (Precedence::kTernary >= parentPrecedence) {
2235 this->write(")");
2236 }
2237}

◆ writeThreadgroupInit()

void SkSL::MetalCodeGenerator::writeThreadgroupInit ( )
protected

Definition at line 3460 of file SkSLMetalCodeGenerator.cpp.

3460 {
3461 class : public ThreadgroupStructVisitor {
3462 public:
3463 void visitNonconstantVariable(const Variable& var) override {
3464 this->addElement();
3465 fCodeGen->write("{}");
3466 }
3467 void addElement() {
3468 if (fFirst) {
3469 fCodeGen->write("threadgroup Threadgroups _threadgroups{");
3470 fFirst = false;
3471 } else {
3472 fCodeGen->write(", ");
3473 }
3474 }
3475 void finish() {
3476 if (!fFirst) {
3477 fCodeGen->writeLine("};");
3478 fCodeGen->writeLine("(void)_threadgroups;");
3479 }
3480 }
3481 MetalCodeGenerator* fCodeGen = nullptr;
3482 bool fFirst = true;
3483 } visitor;
3484
3485 visitor.fCodeGen = this;
3486 this->visitThreadgroupStruct(&visitor);
3487 visitor.finish();
3488}
void visitThreadgroupStruct(ThreadgroupStructVisitor *visitor)

◆ writeThreadgroupStruct()

void SkSL::MetalCodeGenerator::writeThreadgroupStruct ( )
protected

Definition at line 3426 of file SkSLMetalCodeGenerator.cpp.

3426 {
3427 class : public ThreadgroupStructVisitor {
3428 public:
3429 void visitNonconstantVariable(const Variable& var) override {
3430 this->addElement();
3431 fCodeGen->write(" ");
3432 fCodeGen->writeModifiers(var.modifierFlags());
3433 fCodeGen->writeType(var.type());
3434 fCodeGen->write(" ");
3435 fCodeGen->writeName(var.mangledName());
3436 fCodeGen->write(";\n");
3437 }
3438 void addElement() {
3439 if (fFirst) {
3440 fCodeGen->write("struct Threadgroups {\n");
3441 fFirst = false;
3442 }
3443 }
3444 void finish() {
3445 if (!fFirst) {
3446 fCodeGen->writeLine("};");
3447 fFirst = true;
3448 }
3449 }
3450
3451 MetalCodeGenerator* fCodeGen = nullptr;
3452 bool fFirst = true;
3453 } visitor;
3454
3455 visitor.fCodeGen = this;
3456 this->visitThreadgroupStruct(&visitor);
3457 visitor.finish();
3458}

◆ writeType()

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

Definition at line 478 of file SkSLMetalCodeGenerator.cpp.

478 {
479 this->write(this->typeName(type));
480}

◆ writeUniformStruct()

void SkSL::MetalCodeGenerator::writeUniformStruct ( )
protected

Definition at line 3089 of file SkSLMetalCodeGenerator.cpp.

3089 {
3090 for (const ProgramElement* e : fProgram.elements()) {
3091 if (e->is<GlobalVarDeclaration>()) {
3092 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
3093 const Variable& var = *decls.varDeclaration().var();
3094 if (var.modifierFlags().isUniform()) {
3095 SkASSERT(var.type().typeKind() != Type::TypeKind::kSampler &&
3096 var.type().typeKind() != Type::TypeKind::kTexture);
3097 int uniformSet = this->getUniformSet(var.layout());
3098 // Make sure that the program's uniform-set value is consistent throughout.
3099 if (-1 == fUniformBuffer) {
3100 this->write("struct Uniforms {\n");
3101 fUniformBuffer = uniformSet;
3102 } else if (uniformSet != fUniformBuffer) {
3103 fContext.fErrors->error(decls.fPosition,
3104 "Metal backend requires all uniforms to have the same "
3105 "'layout(set=...)'");
3106 }
3107 this->write(" ");
3108 this->writeType(var.type());
3109 this->write(" ");
3110 this->writeName(var.mangledName());
3111 this->write(";\n");
3112 }
3113 }
3114 }
3115 if (-1 != fUniformBuffer) {
3116 this->write("};\n");
3117 }
3118}
int getUniformSet(const Layout &layout)

◆ writeVarDeclaration()

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

Definition at line 2846 of file SkSLMetalCodeGenerator.cpp.

2846 {
2847 this->writeModifiers(varDecl.var()->modifierFlags());
2848 this->writeType(varDecl.var()->type());
2849 this->write(" ");
2850 this->writeName(varDecl.var()->mangledName());
2851 if (varDecl.value()) {
2852 this->write(" = ");
2853 this->writeVarInitializer(*varDecl.var(), *varDecl.value());
2854 }
2855 this->write(";");
2856}
void writeVarInitializer(const Variable &var, const Expression &value)

◆ writeVariableReference()

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

Definition at line 1764 of file SkSLMetalCodeGenerator.cpp.

1764 {
1765 switch (ref.variable()->layout().fBuiltin) {
1767 this->write("_out.sk_FragColor");
1768 break;
1770 this->write("_out.sk_SampleMask");
1771 break;
1774 this->write("_out.sk_SecondaryFragColor");
1775 } else {
1776 fContext.fErrors->error(ref.position(), "'sk_SecondaryFragColor' not supported");
1777 }
1778 break;
1780 this->writeFragCoord();
1781 break;
1783 this->write("sk_SampleMaskIn");
1784 break;
1786 this->write("sk_VertexID");
1787 break;
1789 this->write("sk_InstanceID");
1790 break;
1792 // We'd set the front facing winding in the MTLRenderCommandEncoder to be counter
1793 // clockwise to match Skia convention.
1794 if (!fRTFlipName.empty()) {
1795 this->write("(" + fRTFlipName + ".y < 0 ? _frontFacing : !_frontFacing)");
1796 } else {
1797 this->write("_frontFacing");
1798 }
1799 break;
1803 } else {
1804 fContext.fErrors->error(ref.position(), "'sk_LastFragColor' not supported");
1805 }
1806 break;
1807 default:
1808 const Variable& var = *ref.variable();
1809 if (var.storage() == Variable::Storage::kGlobal) {
1810 if (is_input(var)) {
1811 this->write("_in.");
1812 } else if (is_output(var)) {
1813 this->write("_out.");
1814 } else if (is_uniforms(var)) {
1815 this->write("_uniforms.");
1816 } else if (is_threadgroup(var)) {
1817 this->write("_threadgroups.");
1818 } else if (is_in_globals(var)) {
1819 this->write("_globals.");
1820 }
1821 }
1822 this->writeName(var.mangledName());
1823 }
1824}
constexpr int SK_CLOCKWISE_BUILTIN
Definition: SkSLCompiler.h:31
constexpr int SK_FRAGCOLOR_BUILTIN
Definition: SkSLCompiler.h:27
constexpr int SK_LASTFRAGCOLOR_BUILTIN
Definition: SkSLCompiler.h:28
constexpr int SK_SECONDARYFRAGCOLOR_BUILTIN
Definition: SkSLCompiler.h:29
bool fDualSourceBlendingSupport
Definition: SkSLUtil.h:84

◆ writeVarInitializer()

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

Definition at line 2835 of file SkSLMetalCodeGenerator.cpp.

2835 {
2836 this->writeExpression(value, Precedence::kExpression);
2837}

◆ writeWithIndexSubstitution()

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

Definition at line 572 of file SkSLMetalCodeGenerator.cpp.

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

Member Data Documentation

◆ fAnonInterfaceCount

int SkSL::MetalCodeGenerator::fAnonInterfaceCount = 0
protected

Definition at line 329 of file SkSLMetalCodeGenerator.cpp.

◆ fAtLineStart

bool SkSL::MetalCodeGenerator::fAtLineStart = false
protected

Definition at line 337 of file SkSLMetalCodeGenerator.cpp.

◆ fCurrentFunction

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

Definition at line 344 of file SkSLMetalCodeGenerator.cpp.

◆ fExtraFunctionPrototypes

StringStream SkSL::MetalCodeGenerator::fExtraFunctionPrototypes
protected

Definition at line 334 of file SkSLMetalCodeGenerator.cpp.

◆ fExtraFunctions

StringStream SkSL::MetalCodeGenerator::fExtraFunctions
protected

Definition at line 333 of file SkSLMetalCodeGenerator.cpp.

◆ fFoundDerivatives

bool SkSL::MetalCodeGenerator::fFoundDerivatives = false
protected

Definition at line 339 of file SkSLMetalCodeGenerator.cpp.

◆ fFunctionHeader

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

Definition at line 332 of file SkSLMetalCodeGenerator.cpp.

◆ fHelpers

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

Definition at line 341 of file SkSLMetalCodeGenerator.cpp.

◆ fIndentation

int SkSL::MetalCodeGenerator::fIndentation = 0
protected

Definition at line 336 of file SkSLMetalCodeGenerator.cpp.

◆ fIndexSubstitutionData

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

Definition at line 362 of file SkSLMetalCodeGenerator.cpp.

◆ fInterfaceBlockNameMap

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

Definition at line 328 of file SkSLMetalCodeGenerator.cpp.

◆ fLineEnding

const char* SkSL::MetalCodeGenerator::fLineEnding
protected

Definition at line 331 of file SkSLMetalCodeGenerator.cpp.

◆ fPaddingCount

int SkSL::MetalCodeGenerator::fPaddingCount = 0
protected

Definition at line 330 of file SkSLMetalCodeGenerator.cpp.

◆ fRequirements

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

Definition at line 340 of file SkSLMetalCodeGenerator.cpp.

◆ fReservedWords

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

Definition at line 327 of file SkSLMetalCodeGenerator.cpp.

◆ fRTFlipName

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

Definition at line 343 of file SkSLMetalCodeGenerator.cpp.

◆ fSwizzleHelperCount

int SkSL::MetalCodeGenerator::fSwizzleHelperCount = 0
protected

Definition at line 345 of file SkSLMetalCodeGenerator.cpp.

◆ fUniformBuffer

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

Definition at line 342 of file SkSLMetalCodeGenerator.cpp.

◆ fVarCount

int SkSL::MetalCodeGenerator::fVarCount = 0
protected

Definition at line 335 of file SkSLMetalCodeGenerator.cpp.

◆ fWrittenInverse2

bool SkSL::MetalCodeGenerator::fWrittenInverse2 = false
protected

Definition at line 365 of file SkSLMetalCodeGenerator.cpp.

◆ fWrittenInverse3

bool SkSL::MetalCodeGenerator::fWrittenInverse3 = false
protected

Definition at line 365 of file SkSLMetalCodeGenerator.cpp.

◆ fWrittenInverse4

bool SkSL::MetalCodeGenerator::fWrittenInverse4 = false
protected

Definition at line 365 of file SkSLMetalCodeGenerator.cpp.

◆ fWrittenMatrixCompMult

bool SkSL::MetalCodeGenerator::fWrittenMatrixCompMult = false
protected

Definition at line 366 of file SkSLMetalCodeGenerator.cpp.

◆ fWrittenOuterProduct

bool SkSL::MetalCodeGenerator::fWrittenOuterProduct = false
protected

Definition at line 367 of file SkSLMetalCodeGenerator.cpp.

◆ kFragCoord_Requirement

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

Definition at line 115 of file SkSLMetalCodeGenerator.cpp.

◆ kGlobals_Requirement

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

Definition at line 114 of file SkSLMetalCodeGenerator.cpp.

◆ kInputs_Requirement

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

Definition at line 111 of file SkSLMetalCodeGenerator.cpp.

◆ kInstanceID_Requirement

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

Definition at line 118 of file SkSLMetalCodeGenerator.cpp.

◆ kNo_Requirements

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

Definition at line 110 of file SkSLMetalCodeGenerator.cpp.

◆ kOutputs_Requirement

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

Definition at line 112 of file SkSLMetalCodeGenerator.cpp.

◆ kSampleMaskIn_Requirement

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

Definition at line 116 of file SkSLMetalCodeGenerator.cpp.

◆ kSamplerSuffix

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

Definition at line 347 of file SkSLMetalCodeGenerator.cpp.

◆ kTextureSuffix

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

Definition at line 346 of file SkSLMetalCodeGenerator.cpp.

◆ kThreadgroups_Requirement

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

Definition at line 119 of file SkSLMetalCodeGenerator.cpp.

◆ kUniforms_Requirement

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

Definition at line 113 of file SkSLMetalCodeGenerator.cpp.

◆ kVertexID_Requirement

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

Definition at line 117 of file SkSLMetalCodeGenerator.cpp.


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