Add WebGPU specific validation for multiple BuiltIn decorations (#2333)

Covers NumWorkgroups, LocalInvocationId & GlobalInvocationId

Part of resolving #2276
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index 729029a..efe9b09 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -2446,13 +2446,15 @@
 
 spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
-  if (spvIsVulkanEnv(_.context()->target_env)) {
+  if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
     if (spv_result_t error = ValidateI32Vec(
             decoration, inst, 3,
             [this, &decoration,
              &inst](const std::string& message) -> spv_result_t {
               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
-                     << "According to the Vulkan spec BuiltIn "
+                     << "According to the "
+                     << spvLogStringForEnv(_.context()->target_env)
+                     << " spec BuiltIn "
                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
                                                       decoration.params()[0])
                      << " variable needs to be a 3-component 32-bit int "
@@ -2472,12 +2474,13 @@
     const Decoration& decoration, const Instruction& built_in_inst,
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
-  if (spvIsVulkanEnv(_.context()->target_env)) {
+  if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
     if (storage_class != SpvStorageClassMax &&
         storage_class != SpvStorageClassInput) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
-             << "Vulkan spec allows BuiltIn "
+             << spvLogStringForEnv(_.context()->target_env)
+             << " spec allows BuiltIn "
              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
                                               decoration.params()[0])
              << " to be only used for variables with Input storage class. "
@@ -2487,11 +2490,15 @@
     }
 
     for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelGLCompute &&
-          execution_model != SpvExecutionModelTaskNV &&
-          execution_model != SpvExecutionModelMeshNV) {
+      bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute ||
+                              execution_model == SpvExecutionModelTaskNV ||
+                              execution_model == SpvExecutionModelMeshNV;
+      bool has_webgpu_model = execution_model == SpvExecutionModelGLCompute;
+      if ((spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) ||
+          (spvIsWebGPUEnv(_.context()->target_env) && !has_webgpu_model)) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
-               << "Vulkan spec allows BuiltIn "
+               << spvLogStringForEnv(_.context()->target_env)
+               << " spec allows BuiltIn "
                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
                                                 decoration.params()[0])
                << " to be used only with GLCompute execution model. "
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp
index 2560b94..ac01920 100644
--- a/test/val/val_builtins_test.cpp
+++ b/test/val/val_builtins_test.cpp
@@ -745,6 +745,13 @@
             Values(TestResult())));
 
 INSTANTIATE_TEST_SUITE_P(
+    ComputeShaderInputInt32Vec3Success,
+    ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
+    Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"),
+            Values("GLCompute"), Values("Input"), Values("%u32vec3"),
+            Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
     ComputeShaderInputInt32Vec3NotGLCompute,
     ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
     Combine(
@@ -757,6 +764,15 @@
                           "to be used only with GLCompute execution model"))));
 
 INSTANTIATE_TEST_SUITE_P(
+    ComputeShaderInputInt32Vec3NotGLCompute,
+    ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
+    Combine(
+        Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"),
+        Values("Vertex", "Fragment"), Values("Input"), Values("%u32vec3"),
+        Values(TestResult(SPV_ERROR_INVALID_DATA,
+                          "to be used only with GLCompute execution model"))));
+
+INSTANTIATE_TEST_SUITE_P(
     ComputeShaderInputInt32Vec3NotInput,
     ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
     Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups",
@@ -768,6 +784,16 @@
                 "uses storage class Output"))));
 
 INSTANTIATE_TEST_SUITE_P(
+    ComputeShaderInputInt32Vec3NotInput,
+    ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
+    Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"),
+            Values("GLCompute"), Values("Output"), Values("%u32vec3"),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "to be only used for variables with Input storage class",
+                "uses storage class Output"))));
+
+INSTANTIATE_TEST_SUITE_P(
     ComputeShaderInputInt32Vec3NotIntVector,
     ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
     Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups",
@@ -779,6 +805,16 @@
                               "is not an int vector"))));
 
 INSTANTIATE_TEST_SUITE_P(
+    ComputeShaderInputInt32Vec3NotIntVector,
+    ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
+    Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"),
+            Values("GLCompute"), Values("Input"),
+            Values("%u32arr3", "%f32vec3"),
+            Values(TestResult(SPV_ERROR_INVALID_DATA,
+                              "needs to be a 3-component 32-bit int vector",
+                              "is not an int vector"))));
+
+INSTANTIATE_TEST_SUITE_P(
     ComputeShaderInputInt32Vec3NotIntVec3,
     ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
     Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups",
@@ -789,6 +825,15 @@
                               "has 4 components"))));
 
 INSTANTIATE_TEST_SUITE_P(
+    ComputeShaderInputInt32Vec3NotIntVec3,
+    ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
+    Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"),
+            Values("GLCompute"), Values("Input"), Values("%u32vec4"),
+            Values(TestResult(SPV_ERROR_INVALID_DATA,
+                              "needs to be a 3-component 32-bit int vector",
+                              "has 4 components"))));
+
+INSTANTIATE_TEST_SUITE_P(
     ComputeShaderInputInt32Vec3NotInt32Vec,
     ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
     Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups",