SkSL now supports constant 1/0 in all swizzle channels, as well as "LTRB" as an alias for "xyzw" / "rgba".

Bug: skia:9181
Change-Id: Iedefbb94bbb05ce37fcf66ca0b40c97f2adf7698
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/241276
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index cda3b02..d086627 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -851,24 +851,189 @@
     }
 }
 
-void GLSLCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
-    int last = swizzle.fComponents.back();
-    if (last == SKSL_SWIZZLE_0 || last == SKSL_SWIZZLE_1) {
-        this->writeType(swizzle.fType);
-        this->write("(");
-    }
+void GLSLCodeGenerator::writeConstantSwizzle(const Swizzle& swizzle, const String& constants) {
+    this->writeType(swizzle.fType);
+    this->write("(");
+    this->write(constants);
+    this->write(")");
+}
+
+void GLSLCodeGenerator::writeSwizzleMask(const Swizzle& swizzle, const String& mask) {
     this->writeExpression(*swizzle.fBase, kPostfix_Precedence);
     this->write(".");
+    this->write(mask);
+}
+
+void GLSLCodeGenerator::writeSwizzleConstructor(const Swizzle& swizzle, const String& constants,
+                                                const String& mask,
+                                                GLSLCodeGenerator::SwizzleOrder order) {
+    this->writeType(swizzle.fType);
+    this->write("(");
+    if (order == SwizzleOrder::CONSTANTS_FIRST) {
+        this->write(constants);
+        this->write(", ");
+        this->writeSwizzleMask(swizzle, mask);
+    } else {
+        this->writeSwizzleMask(swizzle, mask);
+        this->write(", ");
+        this->write(constants);
+    }
+    this->write(")");
+}
+
+void GLSLCodeGenerator::writeSwizzleConstructor(const Swizzle& swizzle, const String& constants,
+                                                const String& mask, const String& reswizzle) {
+    this->writeSwizzleConstructor(swizzle, constants, mask, SwizzleOrder::MASK_FIRST);
+    this->write(".");
+    this->write(reswizzle);
+}
+
+// Writing a swizzle is complicated due to the handling of constant swizzle components. The most
+// problematic case is a mask like '.r00a'. A naive approach might turn that into
+// 'vec4(base.r, 0, 0, base.a)', but that would cause 'base' to be evaluated twice. We instead
+// group the swizzle mask ('ra') and constants ('0, 0') together and use a secondary swizzle to put
+// them back into the right order, so in this case we end up with something like
+// 'vec4(base4.ra, 0, 0).rbag'.
+void GLSLCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
+    // has a 1 bit in every position for which the swizzle mask is a constant, so 'r0b1' would
+    // yield binary 0101.
+    int constantBits = 0;
+    String mask;
+    String constants;
+    // compute mask ("ra") and constant ("0, 0") strings, and fill in constantBits
     for (int c : swizzle.fComponents) {
-        if (c >= 0) {
-            this->write(&("x\0y\0z\0w\0"[c * 2]));
+        constantBits <<= 1;
+        switch (c) {
+            case SKSL_SWIZZLE_0:
+                constantBits |= 1;
+                if (constants.length() > 0) {
+                    constants += ", ";
+                }
+                constants += "0";
+                break;
+            case SKSL_SWIZZLE_1:
+                constantBits |= 1;
+                if (constants.length() > 0) {
+                    constants += ", ";
+                }
+                constants += "1";
+                break;
+            case 0:
+                mask += "x";
+                break;
+            case 1:
+                mask += "y";
+                break;
+            case 2:
+                mask += "z";
+                break;
+            case 3:
+                mask += "w";
+                break;
+            default:
+                SkASSERT(false);
         }
     }
-    if (last == SKSL_SWIZZLE_0) {
-        this->write(", 0)");
-    }
-    else if (last == SKSL_SWIZZLE_1) {
-        this->write(", 1)");
+    switch (swizzle.fComponents.size()) {
+        case 1:
+            if (constantBits == 1) {
+                this->write(constants);
+            }
+            else {
+                this->writeSwizzleMask(swizzle, mask);
+            }
+            break;
+        case 2:
+            switch (constantBits) {
+                case 0: // 00
+                    this->writeSwizzleMask(swizzle, mask);
+                    break;
+                case 1: // 01
+                    this->writeSwizzleConstructor(swizzle, constants, mask,
+                                                  SwizzleOrder::MASK_FIRST);
+                    break;
+                case 2: // 10
+                    this->writeSwizzleConstructor(swizzle, constants, mask,
+                                                  SwizzleOrder::CONSTANTS_FIRST);
+                    break;
+                case 3: // 11
+                    this->writeConstantSwizzle(swizzle, constants);
+                    break;
+                default:
+                    SkASSERT(false);
+            }
+            break;
+        case 3:
+            switch (constantBits) {
+                case 0: // 000
+                    this->writeSwizzleMask(swizzle, mask);
+                    break;
+                case 1: // 001
+                case 3: // 011
+                    this->writeSwizzleConstructor(swizzle, constants, mask,
+                                                  SwizzleOrder::MASK_FIRST);
+                    break;
+                case 4: // 100
+                case 6: // 110
+                    this->writeSwizzleConstructor(swizzle, constants, mask,
+                                                  SwizzleOrder::CONSTANTS_FIRST);
+                    break;
+                case 2: // 010
+                    this->writeSwizzleConstructor(swizzle, constants, mask, "xzy");
+                    break;
+                case 5: // 101
+                    this->writeSwizzleConstructor(swizzle, constants, mask, "yxz");
+                    break;
+                case 7: // 111
+                    this->writeConstantSwizzle(swizzle, constants);
+                    break;
+            }
+            break;
+        case 4:
+            switch (constantBits) {
+                case  0: // 0000
+                    this->writeSwizzleMask(swizzle, mask);
+                    break;
+                case  1: // 0001
+                case  3: // 0011
+                case  7: // 0111
+                    this->writeSwizzleConstructor(swizzle, constants, mask,
+                                                  SwizzleOrder::MASK_FIRST);
+                    break;
+                case  8: // 1000
+                case 12: // 1100
+                case 14: // 1110
+                    this->writeSwizzleConstructor(swizzle, constants, mask,
+                                                  SwizzleOrder::CONSTANTS_FIRST);
+                    break;
+                case  2: // 0010
+                    this->writeSwizzleConstructor(swizzle, constants, mask, "xywz");
+                    break;
+                case  4: // 0100
+                    this->writeSwizzleConstructor(swizzle, constants, mask, "xwyz");
+                    break;
+                case  5: // 0101
+                    this->writeSwizzleConstructor(swizzle, constants, mask, "xzyw");
+                    break;
+                case  6: // 0110
+                    this->writeSwizzleConstructor(swizzle, constants, mask, "xzwy");
+                    break;
+                case  9: // 1001
+                    this->writeSwizzleConstructor(swizzle, constants, mask, "zxyw");
+                    break;
+                case 10: // 1010
+                    this->writeSwizzleConstructor(swizzle, constants, mask, "zxwy");
+                    break;
+                case 11: // 1011
+                    this->writeSwizzleConstructor(swizzle, constants, mask, "yxzw");
+                    break;
+                case 13: // 1101
+                    this->writeSwizzleConstructor(swizzle, constants, mask, "yzxw");
+                    break;
+                case 15: // 1111
+                    this->writeConstantSwizzle(swizzle, constants);
+                    break;
+            }
     }
 }