Support Dual Source Blending in SPIR-V reader with Tint IR

This patch adds the support of parsing `spv::Decoration::Index` into
the attribute `@blend_src` in the SPIR-V reader with Tint IR.

Bug: chromium:371367697, chromium:341973423
Test: tint_unittests
Change-Id: I6836ad849e4d9b41ec155752ce78e302d9fe511b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/209898
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/ir/disassembler.cc b/src/tint/lang/core/ir/disassembler.cc
index f72a756..67a933c 100644
--- a/src/tint/lang/core/ir/disassembler.cc
+++ b/src/tint/lang/core/ir/disassembler.cc
@@ -914,6 +914,10 @@
             out_ << ", " << StyleAttribute("@location") << "("
                  << StyleLiteral(member->Attributes().location.value()) << ")";
         }
+        if (member->Attributes().blend_src.has_value()) {
+            out_ << ", " << StyleAttribute("@blend_src") << "("
+                 << StyleLiteral(member->Attributes().blend_src.value()) << ")";
+        }
         if (member->Attributes().color.has_value()) {
             out_ << ", " << StyleAttribute("@color") << "("
                  << StyleLiteral(member->Attributes().color.value()) << ")";
diff --git a/src/tint/lang/glsl/writer/raise/shader_io_test.cc b/src/tint/lang/glsl/writer/raise/shader_io_test.cc
index 67cb91a..49c12c7 100644
--- a/src/tint/lang/glsl/writer/raise/shader_io_test.cc
+++ b/src/tint/lang/glsl/writer/raise/shader_io_test.cc
@@ -682,8 +682,8 @@
 
     auto* src = R"(
 Output = struct @align(4) {
-  color1:f32 @offset(0), @location(0)
-  color2:f32 @offset(4), @location(0)
+  color1:f32 @offset(0), @location(0), @blend_src(0)
+  color2:f32 @offset(4), @location(0), @blend_src(1)
 }
 
 %foo = @fragment func():Output {
diff --git a/src/tint/lang/hlsl/writer/raise/shader_io_test.cc b/src/tint/lang/hlsl/writer/raise/shader_io_test.cc
index b75a3c1..1be886c 100644
--- a/src/tint/lang/hlsl/writer/raise/shader_io_test.cc
+++ b/src/tint/lang/hlsl/writer/raise/shader_io_test.cc
@@ -646,8 +646,8 @@
 
     auto* src = R"(
 Output = struct @align(4) {
-  color1:f32 @offset(0), @location(0)
-  color2:f32 @offset(4), @location(0)
+  color1:f32 @offset(0), @location(0), @blend_src(0)
+  color2:f32 @offset(4), @location(0), @blend_src(1)
 }
 
 %foo = @fragment func():Output {
@@ -666,8 +666,8 @@
 }
 
 foo_outputs = struct @align(4) {
-  Output_color1:f32 @offset(0), @location(0)
-  Output_color2:f32 @offset(4), @location(0)
+  Output_color1:f32 @offset(0), @location(0), @blend_src(0)
+  Output_color2:f32 @offset(4), @location(0), @blend_src(1)
 }
 
 %foo_inner = func():Output {
diff --git a/src/tint/lang/msl/writer/raise/shader_io_test.cc b/src/tint/lang/msl/writer/raise/shader_io_test.cc
index 72b76e4..e2eee44 100644
--- a/src/tint/lang/msl/writer/raise/shader_io_test.cc
+++ b/src/tint/lang/msl/writer/raise/shader_io_test.cc
@@ -654,8 +654,8 @@
 
     auto* src = R"(
 Output = struct @align(4) {
-  color1:f32 @offset(0), @location(0)
-  color2:f32 @offset(4), @location(0)
+  color1:f32 @offset(0), @location(0), @blend_src(0)
+  color2:f32 @offset(4), @location(0), @blend_src(1)
 }
 
 %foo = @fragment func():Output {
@@ -674,8 +674,8 @@
 }
 
 foo_outputs = struct @align(4) {
-  Output_color1:f32 @offset(0), @location(0)
-  Output_color2:f32 @offset(4), @location(0)
+  Output_color1:f32 @offset(0), @location(0), @blend_src(0)
+  Output_color2:f32 @offset(4), @location(0), @blend_src(1)
 }
 
 %foo_inner = func():Output {
diff --git a/src/tint/lang/spirv/reader/parser/parser.cc b/src/tint/lang/spirv/reader/parser/parser.cc
index c0e08de..8cd7047 100644
--- a/src/tint/lang/spirv/reader/parser/parser.cc
+++ b/src/tint/lang/spirv/reader/parser/parser.cc
@@ -646,6 +646,9 @@
                 case spv::Decoration::Sample:
                     interpolation().sampling = core::InterpolationSampling::kSample;
                     break;
+                case spv::Decoration::Index:
+                    io_attributes.blend_src = deco->GetSingleWordOperand(2);
+                    break;
                 default:
                     TINT_UNIMPLEMENTED() << "unhandled decoration " << d;
             }
diff --git a/src/tint/lang/spirv/reader/reader_test.cc b/src/tint/lang/spirv/reader/reader_test.cc
index 2b422df..96e76aa 100644
--- a/src/tint/lang/spirv/reader/reader_test.cc
+++ b/src/tint/lang/spirv/reader/reader_test.cc
@@ -350,5 +350,114 @@
 )");
 }
 
+TEST_F(SpirvReaderTest, BlendSrc) {
+    auto got = Run(R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %frag_main "frag_main" %frag_main_loc0_idx0_Output %frag_main_loc0_idx1_Output
+               OpExecutionMode %frag_main OriginUpperLeft
+               OpName %frag_main_loc0_idx0_Output "frag_main_loc0_idx0_Output"
+               OpName %frag_main_loc0_idx1_Output "frag_main_loc0_idx1_Output"
+               OpName %frag_main_inner "frag_main_inner"
+               OpMemberName %FragOutput 0 "color"
+               OpMemberName %FragOutput 1 "blend"
+               OpName %FragOutput "FragOutput"
+               OpName %output "output"
+               OpName %frag_main "frag_main"
+               OpDecorate %frag_main_loc0_idx0_Output Location 0
+               OpDecorate %frag_main_loc0_idx0_Output Index 0
+               OpDecorate %frag_main_loc0_idx1_Output Location 0
+               OpDecorate %frag_main_loc0_idx1_Output Index 1
+               OpMemberDecorate %FragOutput 0 Offset 0
+               OpMemberDecorate %FragOutput 1 Offset 16
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%frag_main_loc0_idx0_Output = OpVariable %_ptr_Output_v4float Output
+%frag_main_loc0_idx1_Output = OpVariable %_ptr_Output_v4float Output
+ %FragOutput = OpTypeStruct %v4float %v4float
+          %8 = OpTypeFunction %FragOutput
+%_ptr_Function_FragOutput = OpTypePointer Function %FragOutput
+         %12 = OpConstantNull %FragOutput
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+  %float_0_5 = OpConstant %float 0.5
+    %float_1 = OpConstant %float 1
+         %17 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_1
+     %uint_1 = OpConstant %uint 1
+       %void = OpTypeVoid
+         %25 = OpTypeFunction %void
+%frag_main_inner = OpFunction %FragOutput None %8
+          %9 = OpLabel
+     %output = OpVariable %_ptr_Function_FragOutput Function %12
+         %13 = OpAccessChain %_ptr_Function_v4float %output %uint_0
+               OpStore %13 %17 None
+         %20 = OpAccessChain %_ptr_Function_v4float %output %uint_1
+               OpStore %20 %17 None
+         %22 = OpLoad %FragOutput %output None
+               OpReturnValue %22
+               OpFunctionEnd
+  %frag_main = OpFunction %void None %25
+         %26 = OpLabel
+         %27 = OpFunctionCall %FragOutput %frag_main_inner
+         %28 = OpCompositeExtract %v4float %27 0
+               OpStore %frag_main_loc0_idx0_Output %28 None
+         %29 = OpCompositeExtract %v4float %27 1
+               OpStore %frag_main_loc0_idx1_Output %29 None
+               OpReturn
+               OpFunctionEnd
+)");
+
+    ASSERT_EQ(got, Success);
+    EXPECT_EQ(got, R"(
+tint_symbol_2 = struct @align(16) {
+  tint_symbol:vec4<f32> @offset(0)
+  tint_symbol_1:vec4<f32> @offset(16)
+}
+
+tint_symbol_5 = struct @align(16) {
+  tint_symbol_3:vec4<f32> @offset(0), @location(0), @blend_src(0)
+  tint_symbol_4:vec4<f32> @offset(16), @location(0), @blend_src(1)
+}
+
+$B1: {  # root
+  %1:ptr<private, vec4<f32>, read_write> = var
+  %2:ptr<private, vec4<f32>, read_write> = var
+}
+
+%3 = func():tint_symbol_2 {
+  $B2: {
+    %4:ptr<function, tint_symbol_2, read_write> = var, tint_symbol_2(vec4<f32>(0.0f))
+    %5:ptr<function, vec4<f32>, read_write> = access %4, 0u
+    store %5, vec4<f32>(0.5f, 0.5f, 0.5f, 1.0f)
+    %6:ptr<function, vec4<f32>, read_write> = access %4, 1u
+    store %6, vec4<f32>(0.5f, 0.5f, 0.5f, 1.0f)
+    %7:tint_symbol_2 = load %4
+    ret %7
+  }
+}
+%frag_main_inner = func():void {
+  $B3: {
+    %9:tint_symbol_2 = call %3
+    %10:vec4<f32> = access %9, 0u
+    store %1, %10
+    %11:vec4<f32> = access %9, 1u
+    store %2, %11
+    ret
+  }
+}
+%frag_main = @fragment func():tint_symbol_5 {
+  $B4: {
+    %13:void = call %frag_main_inner
+    %14:vec4<f32> = load %1
+    %15:vec4<f32> = load %2
+    %16:tint_symbol_5 = construct %14, %15
+    ret %16
+  }
+}
+)");
+}
+
 }  // namespace
 }  // namespace tint::spirv::reader
diff --git a/src/tint/lang/spirv/writer/raise/shader_io_test.cc b/src/tint/lang/spirv/writer/raise/shader_io_test.cc
index 95c8048..d0adc5a 100644
--- a/src/tint/lang/spirv/writer/raise/shader_io_test.cc
+++ b/src/tint/lang/spirv/writer/raise/shader_io_test.cc
@@ -662,8 +662,8 @@
 
     auto* src = R"(
 Output = struct @align(4) {
-  color1:f32 @offset(0), @location(0)
-  color2:f32 @offset(4), @location(0)
+  color1:f32 @offset(0), @location(0), @blend_src(0)
+  color2:f32 @offset(4), @location(0), @blend_src(1)
 }
 
 %foo = @fragment func():Output {