consolidated writing fields logic and added more builtins for skslc msl backend

Bug: skia:
Change-Id: I6cad948bc68194322f031926ab9a49186ec2302b
Reviewed-on: https://skia-review.googlesource.com/134502
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Timothy Liang <timliang@google.com>
diff --git a/src/sksl/SkSLMetalCodeGenerator.cpp b/src/sksl/SkSLMetalCodeGenerator.cpp
index af2366e..5a7ccb7 100644
--- a/src/sksl/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/SkSLMetalCodeGenerator.cpp
@@ -80,14 +80,7 @@
             fWrittenStructs.push_back(&type);
             this->writeLine("struct " + type.name() + " {");
             fIndentation++;
-            for (const auto& f : type.fields()) {
-                this->writeModifiers(f.fModifiers, false);
-                // sizes (which must be static in structs) are part of the type name here
-                this->writeType(*f.fType);
-                this->write(" ");
-                this->writeName(f.fName);
-                this->writeLine(";");
-            }
+            this->writeFields(type.fields(), type.fOffset);
             fIndentation--;
             this->write("}");
             break;
@@ -317,6 +310,12 @@
         case SK_FRAGCOORD_BUILTIN:
             this->writeFragCoord();
             break;
+        case SK_VERTEXID_BUILTIN:
+            this->write("sk_VertexID");
+            break;
+        case SK_INSTANCEID_BUILTIN:
+            this->write("sk_InstanceID");
+            break;
         default:
             if (Variable::kGlobal_Storage == ref.fVariable.fStorage) {
                 if (ref.fVariable.fModifiers.fFlags & Modifiers::kIn_Flag) {
@@ -574,6 +573,8 @@
         }
         if (fProgram.fKind == Program::kFragment_Kind) {
             this->write(", float4 _fragCoord [[position]]");
+        } else if (fProgram.fKind == Program::kVertex_Kind) {
+            this->write(", uint sk_VertexID [[vertex_id]], uint sk_InstanceID [[instance_id]]");
         }
         separator = ", ";
     } else {
@@ -707,58 +708,16 @@
     if ("sk_PerVertex" == intf.fTypeName) {
         return;
     }
-    MemoryLayout memoryLayout(MemoryLayout::k140_Standard);
-    this->write("struct ");
     this->writeModifiers(intf.fVariable.fModifiers, true);
+    this->write("struct ");
     this->writeLine(intf.fTypeName + " {");
-    fIndentation++;
     const Type* structType = &intf.fVariable.fType;
     fWrittenStructs.push_back(structType);
     while (Type::kArray_Kind == structType->kind()) {
         structType = &structType->componentType();
     }
-    int currentOffset = 0;
-    for (const auto& field: structType->fields()) {
-        int fieldOffset = field.fModifiers.fLayout.fOffset;
-        if (fieldOffset != -1) {
-            if (currentOffset > fieldOffset) {
-                ABORT("Original interface offsets are too close for MoltenVK");
-            } else if (currentOffset < fieldOffset) {
-                this->write("char pad");
-                this->write(to_string(fPaddingCount++));
-                this->write("[");
-                this->write(to_string(fieldOffset - currentOffset));
-                this->writeLine("];");
-                currentOffset = fieldOffset;
-            }
-        }
-        const Type* fieldType = field.fType;
-        if (fieldType->kind() == Type::kVector_Kind &&
-            fieldType->columns() == 3) {
-            // Pack all vec3 types so that their size in bytes will match what was expected in the
-            // original SkSL code since MSL has vec3 sizes equal to 4 * component type, while SkSL
-            // has vec3 equal to 3 * component type.
-            this->write(PACKED_PREFIX);
-        }
-        currentOffset += memoryLayout.size(*fieldType);
-        std::vector<int> sizes;
-        while (fieldType->kind() == Type::kArray_Kind) {
-            sizes.push_back(fieldType->columns());
-            fieldType = &fieldType->componentType();
-        }
-        this->writeType(*fieldType);
-        this->write(" ");
-        this->writeName(field.fName);
-        for (int s : sizes) {
-            if (s <= 0) {
-                this->write("[]");
-            } else {
-                this->write("[" + to_string(s) + "]");
-            }
-        }
-        this->writeLine(";");
-        fInterfaceBlockMap[&field] = &intf;
-    }
+    fIndentation++;
+    writeFields(structType->fields(), structType->fOffset, &intf);
     if (fProgram.fKind == Program::kFragment_Kind) {
         this->writeLine("float u_skRTHeight;");
     }
@@ -781,6 +740,64 @@
     this->writeLine(";");
 }
 
+void MetalCodeGenerator::writeFields(const std::vector<Type::Field>& fields, int parentOffset,
+                                     const InterfaceBlock* parentIntf) {
+    MemoryLayout memoryLayout(MemoryLayout::k140_Standard);
+    int currentOffset = 0;
+    for (const auto& field: fields) {
+        int fieldOffset = field.fModifiers.fLayout.fOffset;
+        const Type* fieldType = field.fType;
+        if (fieldOffset != -1) {
+            if (currentOffset > fieldOffset) {
+                fErrors.error(parentOffset,
+                                "offset of field '" + field.fName + "' must be at least " +
+                                to_string((int) currentOffset));
+            } else if (currentOffset < fieldOffset) {
+                this->write("char pad");
+                this->write(to_string(fPaddingCount++));
+                this->write("[");
+                this->write(to_string(fieldOffset - currentOffset));
+                this->writeLine("];");
+                currentOffset = fieldOffset;
+            }
+            int alignment = memoryLayout.alignment(*fieldType);
+            if (fieldOffset % alignment) {
+                fErrors.error(parentOffset,
+                              "offset of field '" + field.fName + "' must be a multiple of " +
+                              to_string((int) alignment));
+            }
+        }
+        if (fieldType->kind() == Type::kVector_Kind &&
+            fieldType->columns() == 3) {
+            // Pack all vec3 types so that their size in bytes will match what was expected in the
+            // original SkSL code since MSL has vec3 sizes equal to 4 * component type, while SkSL
+            // has vec3 equal to 3 * component type.
+            this->write(PACKED_PREFIX);
+        }
+        currentOffset += memoryLayout.size(*fieldType);
+        std::vector<int> sizes;
+        while (fieldType->kind() == Type::kArray_Kind) {
+            sizes.push_back(fieldType->columns());
+            fieldType = &fieldType->componentType();
+        }
+        this->writeModifiers(field.fModifiers, false);
+        this->writeType(*fieldType);
+        this->write(" ");
+        this->writeName(field.fName);
+        for (int s : sizes) {
+            if (s <= 0) {
+                this->write("[]");
+            } else {
+                this->write("[" + to_string(s) + "]");
+            }
+        }
+        this->writeLine(";");
+        if (parentIntf) {
+            fInterfaceBlockMap[&field] = parentIntf;
+        }
+    }
+}
+
 void MetalCodeGenerator::writeVarInitializer(const Variable& var, const Expression& value) {
     this->writeExpression(value, kTopLevel_Precedence);
 }
diff --git a/src/sksl/SkSLMetalCodeGenerator.h b/src/sksl/SkSLMetalCodeGenerator.h
index d5a8ca1..49a5bd6 100644
--- a/src/sksl/SkSLMetalCodeGenerator.h
+++ b/src/sksl/SkSLMetalCodeGenerator.h
@@ -80,7 +80,7 @@
     MetalCodeGenerator(const Context* context, const Program* program, ErrorReporter* errors,
                       OutputStream* out)
     : INHERITED(program, errors, out)
-    , fReservedWords({"atan2", "rsqrt", "dfdx", "dfdy"})
+    , fReservedWords({"atan2", "rsqrt", "dfdx", "dfdy", "vertex", "fragment"})
     , fLineEnding("\n")
     , fContext(*context) {
         this->setupIntrinsics();
@@ -136,6 +136,9 @@
 
     void writeInterfaceBlocks();
 
+    void writeFields(const std::vector<Type::Field>& fields, int parentOffset,
+                     const InterfaceBlock* parentIntf = nullptr);
+
     int size(const Type* type, bool isPacked) const;
 
     int alignment(const Type* type, bool isPacked) const;