[spirv-writer] Fragment shaders use OriginUpperLeft

Fixes a validation error

Change-Id: Ie003ac61a10f87f3d0c42ad8cb162da50c1c416b
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/20740
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index ce13f0a..aaf9772 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -162,6 +162,11 @@
       return false;
     }
   }
+  for (const auto& ep : mod_->entry_points()) {
+    if (!GenerateExecutionModes(ep.get())) {
+      return false;
+    }
+  }
 
   return true;
 }
@@ -249,10 +254,8 @@
   if (name.empty()) {
     name = ep->function_name();
   }
-
-  auto id = id_for_func_name(ep->function_name());
+  const auto id = id_for_entry_point(ep);
   if (id == 0) {
-    error_ = "unable to find ID for function: " + ep->function_name();
     return false;
   }
 
@@ -268,6 +271,22 @@
   return true;
 }
 
+bool Builder::GenerateExecutionModes(ast::EntryPoint* ep) {
+  const auto id = id_for_entry_point(ep);
+  if (id == 0) {
+    return false;
+  }
+
+  // WGSL fragment shader origin is upper left
+  if (ep->stage() == ast::PipelineStage::kFragment) {
+    push_preamble(
+        spv::Op::OpExecutionMode,
+        {Operand::Int(id), Operand::Int(SpvExecutionModeOriginUpperLeft)});
+  }
+
+  return true;
+}
+
 uint32_t Builder::GenerateExpression(ast::Expression* expr) {
   if (expr->IsArrayAccessor()) {
     return GenerateAccessorExpression(expr->AsArrayAccessor());
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index 394c5e5..f3a40d2 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -98,6 +98,19 @@
     return func_name_to_id_[name];
   }
 
+  /// Retrieves the id for an entry point function, or 0 if not found.
+  /// Emits an error if not found.
+  /// @param ep the entry point
+  /// @returns 0 on error
+  uint32_t id_for_entry_point(ast::EntryPoint* ep) {
+    auto id = id_for_func_name(ep->function_name());
+    if (id == 0) {
+      error_ = "unable to find ID for function: " + ep->function_name();
+      return 0;
+    }
+    return id;
+  }
+
   /// Iterates over all the instructions in the correct order and calls the
   /// given callback
   /// @param cb the callback to execute
@@ -182,6 +195,10 @@
   /// @param ep the entry point
   /// @returns true if the instruction was generated, false otherwise
   bool GenerateEntryPoint(ast::EntryPoint* ep);
+  /// Generates execution modes for an entry point
+  /// @param ep the entry point
+  /// @returns false on failure
+  bool GenerateExecutionModes(ast::EntryPoint* ep);
   /// Generates an expression
   /// @param expr the expression to generate
   /// @returns the resulting ID of the exp = {};ression or 0 on error
diff --git a/src/writer/spirv/builder_entry_point_test.cc b/src/writer/spirv/builder_entry_point_test.cc
index 71dcfb7..788787b 100644
--- a/src/writer/spirv/builder_entry_point_test.cc
+++ b/src/writer/spirv/builder_entry_point_test.cc
@@ -106,6 +106,20 @@
 // TODO(http://crbug.com/tint/28)
 TEST_F(BuilderTest, DISABLED_EntryPoint_WithInterfaceIds) {}
 
+TEST_F(BuilderTest, ExecutionModel_Fragment_OriginUpperLeft) {
+  ast::EntryPoint ep(ast::PipelineStage::kFragment, "main", "frag_main");
+
+  ast::Module mod;
+  Builder b(&mod);
+  b.set_func_name_to_id("frag_main", 2);
+  ASSERT_TRUE(b.GenerateExecutionModes(&ep));
+
+  auto preamble = b.preamble();
+  ASSERT_EQ(preamble.size(), 1u);
+  EXPECT_EQ(DumpInstruction(preamble[0]), R"(OpExecutionMode %2 OriginUpperLeft
+)");
+}
+
 }  // namespace
 }  // namespace spirv
 }  // namespace writer