Add support for the pixel_local modifier to WGSL.

This keyword isn't fully supported by WGSL yet, so it generates
an error when compiled.

https://docs.google.com/document/d/1djJwQLJcVGnDXOA7nhsweppim-hhbqglB4JNyfZcRqQ

Bug: b/299504320
Bug: dawn:1704
Change-Id: Ifb1fcc0192d6aa0087e93de12c269eb38251ff7b
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/753566
Reviewed-by: Arman Uguray <armansito@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
diff --git a/gn/sksl_tests.gni b/gn/sksl_tests.gni
index 2c9bc69..e35e810 100644
--- a/gn/sksl_tests.gni
+++ b/gn/sksl_tests.gni
@@ -417,6 +417,7 @@
   "wgsl/MainHasVoidReturn.sksl",
   "wgsl/MatrixConstructorDiagonal.sksl",
   "wgsl/OutParams.sksl",
+  "wgsl/PixelLocalStorage.sksl",
   "wgsl/Sample.sksl",
   "wgsl/TextureIntrinsics.compute",
   "wgsl/UniformArrays.sksl",
diff --git a/resources/sksl/BUILD.bazel b/resources/sksl/BUILD.bazel
index a00954a..af8643e 100644
--- a/resources/sksl/BUILD.bazel
+++ b/resources/sksl/BUILD.bazel
@@ -1097,6 +1097,7 @@
         "wgsl/MainHasVoidReturn.sksl",
         "wgsl/MatrixConstructorDiagonal.sksl",
         "wgsl/OutParams.sksl",
+        "wgsl/PixelLocalStorage.sksl",
         "wgsl/Sample.sksl",
         "wgsl/TextureIntrinsics.compute",
         "wgsl/UniformArrays.sksl",
diff --git a/resources/sksl/wgsl/PixelLocalStorage.sksl b/resources/sksl/wgsl/PixelLocalStorage.sksl
new file mode 100644
index 0000000..4770cdb
--- /dev/null
+++ b/resources/sksl/wgsl/PixelLocalStorage.sksl
@@ -0,0 +1,11 @@
+struct PixelLocalData {
+    int i;
+    float f;
+};
+
+pixel_local PixelLocalData pls;
+
+void main() {
+    pls.i++;
+    pls.f *= 2.0;
+}
diff --git a/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp b/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp
index 91ff42f..403d71f 100644
--- a/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp
@@ -79,6 +79,7 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <utility>
 
 using namespace skia_private;
 
@@ -717,20 +718,30 @@
 };
 
 WGSLCodeGenerator::ProgramRequirements resolve_program_requirements(const Program* program) {
-    WGSLCodeGenerator::ProgramRequirements::DepsMap dependencies;
+    WGSLCodeGenerator::ProgramRequirements requirements;
 
     for (const ProgramElement* e : program->elements()) {
-        if (!e->is<FunctionDefinition>()) {
-            continue;
+        switch (e->kind()) {
+            case ProgramElement::Kind::kFunction: {
+                const FunctionDeclaration& decl = e->as<FunctionDefinition>().declaration();
+
+                FunctionDependencyResolver resolver(program, &decl, &requirements.fDependencies);
+                requirements.fDependencies.set(&decl, resolver.resolve());
+                break;
+            }
+            case ProgramElement::Kind::kGlobalVar: {
+                const GlobalVarDeclaration& decl = e->as<GlobalVarDeclaration>();
+                if (decl.varDeclaration().var()->modifierFlags().isPixelLocal()) {
+                    requirements.fPixelLocalExtension = true;
+                }
+                break;
+            }
+            default:
+                break;
         }
-
-        const FunctionDeclaration& decl = e->as<FunctionDefinition>().declaration();
-
-        FunctionDependencyResolver resolver(program, &decl, &dependencies);
-        dependencies.set(&decl, resolver.resolve());
     }
 
-    return WGSLCodeGenerator::ProgramRequirements(std::move(dependencies));
+    return requirements;
 }
 
 void collect_pipeline_io_vars(const Program* program,
@@ -912,7 +923,7 @@
 
     {
         AutoOutputStream outputToHeader(this, &fHeader, &fIndentation);
-        this->writeLine("diagnostic(off, derivative_uniformity);");
+        this->writeEnables();
         this->writeStageInputStruct();
         this->writeStageOutputStruct();
         this->writeUniformsAndBuffers();
@@ -1480,7 +1491,7 @@
     // Generate a function call to the user-defined main.
     this->write("_skslMain(");
     auto separator = SkSL::String::Separator();
-    WGSLFunctionDependencies* deps = fRequirements.dependencies.find(&main.declaration());
+    WGSLFunctionDependencies* deps = fRequirements.fDependencies.find(&main.declaration());
     if (deps) {
         if (*deps & WGSLFunctionDependency::kPipelineInputs) {
             this->write(separator());
@@ -3574,10 +3585,9 @@
     switch (e.kind()) {
         case ProgramElement::Kind::kExtension:
             // TODO(skia:13092): WGSL supports extensions via the "enable" directive
-            // (https://www.w3.org/TR/WGSL/#language-extensions). While we could easily emit this
+            // (https://www.w3.org/TR/WGSL/#enable-extensions-sec ). While we could easily emit this
             // directive, we should first ensure that all possible SkSL extension names are
-            // converted to their appropriate WGSL extension. Currently there are no known supported
-            // WGSL extensions aside from the hypotheticals listed in the spec.
+            // converted to their appropriate WGSL extension.
             break;
         case ProgramElement::Kind::kGlobalVar:
             this->writeGlobalVarDeclaration(e.as<GlobalVarDeclaration>());
@@ -3678,6 +3688,8 @@
         this->write("const ");
     } else if (var.modifierFlags().isWorkgroup()) {
         this->write("var<workgroup> ");
+    } else if (var.modifierFlags().isPixelLocal()) {
+        this->write("var<pixel_local> ");
     } else {
         this->write("var<private> ");
     }
@@ -3770,6 +3782,13 @@
     fIndentation--;
 }
 
+void WGSLCodeGenerator::writeEnables() {
+    this->writeLine("diagnostic(off, derivative_uniformity);");
+    if (fRequirements.fPixelLocalExtension) {
+        this->writeLine("enable chromium_experimental_pixel_local;");
+    }
+}
+
 bool WGSLCodeGenerator::needsStageInputStruct() const {
     // It is illegal to declare a struct with no members; we can't emit a placeholder empty stage
     // input struct.
@@ -4008,7 +4027,7 @@
 }
 
 std::string WGSLCodeGenerator::functionDependencyArgs(const FunctionDeclaration& f) {
-    WGSLFunctionDependencies* deps = fRequirements.dependencies.find(&f);
+    WGSLFunctionDependencies* deps = fRequirements.fDependencies.find(&f);
     std::string args;
     if (deps && *deps) {
         const char* separator = "";
@@ -4025,7 +4044,7 @@
 }
 
 bool WGSLCodeGenerator::writeFunctionDependencyParams(const FunctionDeclaration& f) {
-    WGSLFunctionDependencies* deps = fRequirements.dependencies.find(&f);
+    WGSLFunctionDependencies* deps = fRequirements.fDependencies.find(&f);
     if (!deps || !*deps) {
         return false;
     }
diff --git a/src/sksl/codegen/SkSLWGSLCodeGenerator.h b/src/sksl/codegen/SkSLWGSLCodeGenerator.h
index c779974..cb11848 100644
--- a/src/sksl/codegen/SkSLWGSLCodeGenerator.h
+++ b/src/sksl/codegen/SkSLWGSLCodeGenerator.h
@@ -22,7 +22,6 @@
 #include <memory>
 #include <string>
 #include <string_view>
-#include <utility>
 
 namespace SkSL {
 
@@ -127,12 +126,12 @@
         using DepsMap = skia_private::THashMap<const FunctionDeclaration*,
                                                WGSLFunctionDependencies>;
 
-        ProgramRequirements() = default;
-        ProgramRequirements(DepsMap dependencies) : dependencies(std::move(dependencies)) {}
-
         // Mappings used to synthesize function parameters according to dependencies on pipeline
         // input/output variables.
-        DepsMap dependencies;
+        DepsMap fDependencies;
+
+        // These flags track extensions that will need to be enabled.
+        bool fPixelLocalExtension = false;
     };
 
     WGSLCodeGenerator(const Context* context, const Program* program, OutputStream* out)
@@ -306,6 +305,7 @@
     void prepareUniformPolyfillsForInterfaceBlock(const InterfaceBlock* interfaceBlock,
                                                   std::string_view instanceName,
                                                   MemoryLayout::Standard nativeLayout);
+    void writeEnables();
     void writeUniformPolyfills();
 
     void writeTextureOrSampler(const Variable& var,
@@ -339,7 +339,7 @@
     // We assign unique names to anonymous interface blocks based on the type.
     skia_private::THashMap<const Type*, std::string> fInterfaceBlockNameMap;
 
-    // Stores the disallowed identifier names.
+    // Stores the functions which use stage inputs/outputs as well as required WGSL extensions.
     ProgramRequirements fRequirements;
     skia_private::TArray<const Variable*> fPipelineInputs;
     skia_private::TArray<const Variable*> fPipelineOutputs;
diff --git a/src/sksl/ir/SkSLVarDeclarations.cpp b/src/sksl/ir/SkSLVarDeclarations.cpp
index 5f2f0cc..af35d67 100644
--- a/src/sksl/ir/SkSLVarDeclarations.cpp
+++ b/src/sksl/ir/SkSLVarDeclarations.cpp
@@ -261,6 +261,11 @@
                 // Only non-opaque types allow `in` and `out`.
                 permitted |= ModifierFlag::kIn | ModifierFlag::kOut;
             }
+            if (ProgramConfig::IsFragment(context.fConfig->fKind) && baseType->isStruct() &&
+                !baseType->isInterfaceBlock()) {
+                // Only structs in fragment shaders allow `pixel_local`.
+                permitted |= ModifierFlag::kPixelLocal;
+            }
             if (ProgramConfig::IsCompute(context.fConfig->fKind)) {
                 // Only compute shaders allow `workgroup`.
                 if (!baseType->isOpaque() || baseType->isAtomic()) {
diff --git a/tests/sksl/wgsl/PixelLocalStorage.wgsl b/tests/sksl/wgsl/PixelLocalStorage.wgsl
new file mode 100644
index 0000000..1e944a3
--- /dev/null
+++ b/tests/sksl/wgsl/PixelLocalStorage.wgsl
@@ -0,0 +1,25 @@
+### Compilation failed:
+
+error: :2:1 error: chromium_experimental_pixel_local requires TINT_ENABLE_LOCAL_STORAGE_EXTENSION
+enable chromium_experimental_pixel_local;
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+
+diagnostic(off, derivative_uniformity);
+enable chromium_experimental_pixel_local;
+struct PixelLocalData {
+  i: i32,
+  f: f32,
+};
+var<pixel_local> pls: PixelLocalData;
+fn _skslMain() {
+  {
+    pls.i = pls.i + i32(1);
+    pls.f = pls.f * 2.0;
+  }
+}
+@fragment fn main() {
+  _skslMain();
+}
+
+1 error