Added DSL layout() support

Change-Id: I698dd607ff4676b6fb29be0a718c6073b66dc7c7
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/406336
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
diff --git a/gn/sksl.gni b/gn/sksl.gni
index 28edbd8..edd2401 100644
--- a/gn/sksl.gni
+++ b/gn/sksl.gni
@@ -25,6 +25,7 @@
   "$_include/sksl/DSLErrorHandling.h",
   "$_include/sksl/DSLExpression.h",
   "$_include/sksl/DSLFunction.h",
+  "$_include/sksl/DSLLayout.h",
   "$_include/sksl/DSLModifiers.h",
   "$_include/sksl/DSLRuntimeEffects.h",
   "$_include/sksl/DSLStatement.h",
@@ -82,6 +83,7 @@
   "$_src/sksl/dsl/DSLCore.cpp",
   "$_src/sksl/dsl/DSLExpression.cpp",
   "$_src/sksl/dsl/DSLFunction.cpp",
+  "$_src/sksl/dsl/DSLLayout.cpp",
   "$_src/sksl/dsl/DSLRuntimeEffects.cpp",
   "$_src/sksl/dsl/DSLStatement.cpp",
   "$_src/sksl/dsl/DSLType.cpp",
diff --git a/include/sksl/DSL.h b/include/sksl/DSL.h
index 4c6cbb2..74f313b 100644
--- a/include/sksl/DSL.h
+++ b/include/sksl/DSL.h
@@ -19,6 +19,7 @@
 using Expression = DSLExpression;
 using Field = DSLField;
 using Function = DSLFunction;
+using Layout = DSLLayout;
 using Modifiers = DSLModifiers;
 using Statement = DSLStatement;
 using Var = DSLVar;
diff --git a/include/sksl/DSLLayout.h b/include/sksl/DSLLayout.h
new file mode 100644
index 0000000..e237ada
--- /dev/null
+++ b/include/sksl/DSLLayout.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_DSL_LAYOUT
+#define SKSL_DSL_LAYOUT
+
+#include "include/sksl/DSLLayout.h"
+
+#include "include/private/SkSLLayout.h"
+#include "include/sksl/DSLErrorHandling.h"
+
+namespace SkSL {
+
+namespace dsl {
+
+class DSLLayout {
+public:
+    DSLLayout& originUpperLeft(PositionInfo pos = PositionInfo()) {
+        return this->flag(SkSL::Layout::kOriginUpperLeft_Flag, "origin_upper_left", pos);
+    }
+
+    DSLLayout& overrideCoverage(PositionInfo pos = PositionInfo()) {
+        return this->flag(SkSL::Layout::kOverrideCoverage_Flag, "override_coverage", pos);
+    }
+
+    DSLLayout& pushConstant(PositionInfo pos = PositionInfo()) {
+        return this->flag(SkSL::Layout::kPushConstant_Flag, "push_constant", pos);
+    }
+
+    DSLLayout& blendSupportAllEquations(PositionInfo pos = PositionInfo()) {
+        return this->flag(SkSL::Layout::kBlendSupportAllEquations_Flag,
+                          "blend_support_all_equations", pos);
+    }
+
+    DSLLayout& srgbUnpremul(PositionInfo pos = PositionInfo()) {
+        return this->flag(SkSL::Layout::kSRGBUnpremul_Flag, "srgb_unpremul", pos);
+    }
+
+    DSLLayout& location(int location, PositionInfo pos = PositionInfo()) {
+        return this->intValue(&fSkSLLayout.fLocation, location, SkSL::Layout::kLocation_Flag,
+                              "location", pos);
+    }
+
+    DSLLayout& offset(int offset, PositionInfo pos = PositionInfo()) {
+        return this->intValue(&fSkSLLayout.fOffset, offset, SkSL::Layout::kOffset_Flag, "offset",
+                              pos);
+    }
+
+    DSLLayout& binding(int binding, PositionInfo pos = PositionInfo()) {
+        return this->intValue(&fSkSLLayout.fBinding, binding, SkSL::Layout::kBinding_Flag,
+                              "binding", pos);
+    }
+
+    DSLLayout& index(int index, PositionInfo pos = PositionInfo()) {
+        return this->intValue(&fSkSLLayout.fIndex, index, SkSL::Layout::kIndex_Flag, "index", pos);
+    }
+
+    DSLLayout& set(int set, PositionInfo pos = PositionInfo()) {
+        return this->intValue(&fSkSLLayout.fSet, set, SkSL::Layout::kSet_Flag, "set", pos);
+    }
+
+    DSLLayout& builtin(int builtin, PositionInfo pos = PositionInfo()) {
+        return this->intValue(&fSkSLLayout.fBuiltin, builtin, SkSL::Layout::kBuiltin_Flag,
+                              "builtin", pos);
+    }
+
+    DSLLayout& inputAttachmentIndex(int inputAttachmentIndex, PositionInfo pos = PositionInfo()) {
+        return this->intValue(&fSkSLLayout.fInputAttachmentIndex, inputAttachmentIndex,
+                              SkSL::Layout::kInputAttachmentIndex_Flag, "input_attachment_index",
+                              pos);
+    }
+
+private:
+    DSLLayout& flag(SkSL::Layout::Flag mask, const char* name, PositionInfo pos);
+
+    DSLLayout& intValue(int* target, int value, SkSL::Layout::Flag flag, const char* name,
+                        PositionInfo pos);
+
+    SkSL::Layout fSkSLLayout;
+
+    friend class DSLModifiers;
+};
+
+} // namespace dsl
+
+} // namespace SkSL
+
+#endif
diff --git a/include/sksl/DSLModifiers.h b/include/sksl/DSLModifiers.h
index 70f142c..262ef20 100644
--- a/include/sksl/DSLModifiers.h
+++ b/include/sksl/DSLModifiers.h
@@ -10,6 +10,7 @@
 
 #include "include/private/SkSLModifiers.h"
 #include "include/private/SkTArray.h"
+#include "include/sksl/DSLLayout.h"
 
 namespace SkSL {
 
@@ -32,10 +33,11 @@
 class DSLModifiers {
 public:
 
-    DSLModifiers() {}
+    DSLModifiers(int flags = 0)
+        : DSLModifiers(DSLLayout(), flags) {}
 
-    DSLModifiers(int flags)
-        : fModifiers(SkSL::Layout(), flags) {}
+    DSLModifiers(DSLLayout layout, int flags = 0)
+        : fModifiers(layout.fSkSLLayout, flags) {}
 
     int flags() const {
         return fModifiers.fFlags;
diff --git a/src/sksl/dsl/DSLLayout.cpp b/src/sksl/dsl/DSLLayout.cpp
new file mode 100644
index 0000000..3d70e60
--- /dev/null
+++ b/src/sksl/dsl/DSLLayout.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/sksl/DSLLayout.h"
+
+#include "src/sksl/dsl/priv/DSLWriter.h"
+
+namespace SkSL {
+
+namespace dsl {
+
+DSLLayout& DSLLayout::flag(SkSL::Layout::Flag mask, const char* name, PositionInfo pos) {
+    if (fSkSLLayout.fFlags & mask) {
+        DSLWriter::ReportError(("error: layout qualifier '" + String(name) +
+                                "' appears more than once\n").c_str(), &pos);
+    }
+    fSkSLLayout.fFlags |= mask;
+    return *this;
+}
+
+DSLLayout& DSLLayout::intValue(int* target, int value, SkSL::Layout::Flag flag, const char* name,
+                               PositionInfo pos) {
+    this->flag(flag, name, pos);
+    *target = value;
+    return *this;
+}
+
+} // namespace dsl
+
+} // namespace SkSL
diff --git a/tests/SkSLDSLTest.cpp b/tests/SkSLDSLTest.cpp
index c73fc4c..bd07534 100644
--- a/tests/SkSLDSLTest.cpp
+++ b/tests/SkSLDSLTest.cpp
@@ -1763,6 +1763,95 @@
     // Uniforms do not need to be explicitly declared
 }
 
+DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLLayout, r, ctxInfo) {
+    AutoDSLContext context(ctxInfo.directContext()->priv().getGpu(), /*markVarsDeclared=*/false);
+    Var v1(DSLModifiers(DSLLayout().location(1).set(2).binding(3).offset(4).index(5).builtin(6)
+                                   .inputAttachmentIndex(7),
+                        kConst_Modifier), kInt_Type, "v1", 0);
+    EXPECT_EQUAL(Declare(v1), "layout (location = 1, offset = 4, binding = 3, index = 5, set = 2, "
+                              "builtin = 6, input_attachment_index = 7) const int v1 = 0;");
+
+    Var v2(DSLLayout().originUpperLeft(), kFloat2_Type, "v2");
+    EXPECT_EQUAL(Declare(v2), "layout (origin_upper_left) float2 v2;");
+
+    Var v3(DSLLayout().overrideCoverage(), kHalf_Type, "v3");
+    EXPECT_EQUAL(Declare(v3), "layout (override_coverage) half v3;");
+
+    Var v4(DSLLayout().pushConstant(), kBool_Type, "v4");
+    EXPECT_EQUAL(Declare(v4), "layout (push_constant) bool v4;");
+
+    Var v5(DSLLayout().blendSupportAllEquations(), kHalf4_Type, "v5");
+    EXPECT_EQUAL(Declare(v5), "layout (blend_support_all_equations) half4 v5;");
+
+    Var v6(DSLModifiers(DSLLayout().srgbUnpremul(), kUniform_Modifier), kBool_Type, "v6");
+    DeclareGlobal(v6);
+    EXPECT_EQUAL(*DSLWriter::ProgramElements()[0], "layout (srgb_unpremul) uniform bool v6;");
+
+    {
+        ExpectError error(r, "error: layout qualifier 'location' appears more than once\n");
+        DSLLayout().location(1).location(2);
+    }
+
+    {
+        ExpectError error(r, "error: layout qualifier 'set' appears more than once\n");
+        DSLLayout().set(1).set(2);
+    }
+
+    {
+        ExpectError error(r, "error: layout qualifier 'binding' appears more than once\n");
+        DSLLayout().binding(1).binding(2);
+    }
+
+    {
+        ExpectError error(r, "error: layout qualifier 'offset' appears more than once\n");
+        DSLLayout().offset(1).offset(2);
+    }
+
+    {
+        ExpectError error(r, "error: layout qualifier 'index' appears more than once\n");
+        DSLLayout().index(1).index(2);
+    }
+
+    {
+        ExpectError error(r, "error: layout qualifier 'builtin' appears more than once\n");
+        DSLLayout().builtin(1).builtin(2);
+    }
+
+    {
+        ExpectError error(r, "error: layout qualifier 'input_attachment_index' appears more than "
+                             "once\n");
+        DSLLayout().inputAttachmentIndex(1).inputAttachmentIndex(2);
+    }
+
+    {
+        ExpectError error(r, "error: layout qualifier 'origin_upper_left' appears more than "
+                             "once\n");
+        DSLLayout().originUpperLeft().originUpperLeft();
+    }
+
+    {
+        ExpectError error(r, "error: layout qualifier 'override_coverage' appears more than "
+                             "once\n");
+        DSLLayout().overrideCoverage().overrideCoverage();
+    }
+
+    {
+        ExpectError error(r, "error: layout qualifier 'push_constant' appears more than once\n");
+        DSLLayout().pushConstant().pushConstant();
+    }
+
+    {
+        ExpectError error(r, "error: layout qualifier 'blend_support_all_equations' appears more "
+                             "than once\n");
+        DSLLayout().blendSupportAllEquations().blendSupportAllEquations();
+    }
+
+    {
+        ExpectError error(r, "error: layout qualifier 'srgb_unpremul' appears more than once\n");
+        DSLLayout().srgbUnpremul().srgbUnpremul();
+    }
+}
+
 DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLSampleFragmentProcessor, r, ctxInfo) {
     AutoDSLContext context(ctxInfo.directContext()->priv().getGpu(), /*markVarsDeclared=*/true,
                            SkSL::ProgramKind::kFragmentProcessor);