Support Dual Source Blending in SPIR-V reader with Tint AST
This patch adds the support of parsing `spv::Decoration::Index` into
the attribute `@blend_src` in the SPIR-V reader with Tint AST.
Fixed: chromium:371367697, chromium:341973423
Test: tint_unittests
Change-Id: I927d95b523e599be6cd2962087d39d081ad2aa5d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/210174
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/spirv/reader/ast_parser/ast_parser.cc b/src/tint/lang/spirv/reader/ast_parser/ast_parser.cc
index 54d54b9..58c9ef9 100644
--- a/src/tint/lang/spirv/reader/ast_parser/ast_parser.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/ast_parser.cc
@@ -270,6 +270,7 @@
}
switch (static_cast<spv::Decoration>(deco[0])) {
case spv::Decoration::Location:
+ case spv::Decoration::Index:
case spv::Decoration::Flat:
case spv::Decoration::NoPerspective:
case spv::Decoration::Centroid:
@@ -589,6 +590,7 @@
deco_mgr_ = nullptr;
glsl_std_450_imports_.clear();
+ enabled_extensions_.Clear();
}
bool ASTParser::ParseInternalModule() {
@@ -1805,7 +1807,23 @@
}
// The list didn't have a location. Add it.
attributes.Add(replacement);
- return;
+}
+
+void ASTParser::SetBlendSrc(Attributes& attributes, const ast::Attribute* replacement) {
+ if (!replacement) {
+ return;
+ }
+ for (auto*& attribute : attributes.list) {
+ if (attribute->Is<ast::BlendSrcAttribute>()) {
+ // Replace this BlendSrc attribute with the replacement.
+ // The old one doesn't leak because it's kept in the builder's AST node
+ // list.
+ attribute = replacement;
+ return; // Assume there is only one such decoration.
+ }
+ }
+ // The list didn't have a BlendSrc. Add it.
+ attributes.Add(replacement);
}
bool ASTParser::ConvertPipelineDecorations(const Type* store_type,
@@ -1853,6 +1871,14 @@
}
sampling = core::InterpolationSampling::kSample;
break;
+ case spv::Decoration::Index:
+ if (deco.size() != 2) {
+ return Fail()
+ << "malformed Index decoration on ID requires one literal operand";
+ }
+ Enable(wgsl::Extension::kDualSourceBlending);
+ SetBlendSrc(attributes, builder_.BlendSrc(AInt(deco[1])));
+ break;
default:
break;
}
diff --git a/src/tint/lang/spirv/reader/ast_parser/ast_parser.h b/src/tint/lang/spirv/reader/ast_parser/ast_parser.h
index 4174965..760f31b 100644
--- a/src/tint/lang/spirv/reader/ast_parser/ast_parser.h
+++ b/src/tint/lang/spirv/reader/ast_parser/ast_parser.h
@@ -296,6 +296,14 @@
/// @param replacement the location decoration to place into the list
void SetLocation(Attributes& attributes, const ast::Attribute* replacement);
+ /// Updates the attribute list, placing a non-null BlendSrc decoration into
+ /// the list, replacing an existing one if it exists. Does nothing if the
+ /// replacement is nullptr.
+ /// Assumes the list contains at most one BlendSrc decoration.
+ /// @param attributes the attribute list to modify
+ /// @param replacement the BlendSrc decoration to place into the list
+ void SetBlendSrc(Attributes& attributes, const ast::Attribute* replacement);
+
/// Converts a SPIR-V struct member decoration into a number of AST
/// decorations. If the decoration is recognized but deliberately dropped,
/// then returns an empty list without a diagnostic. On failure, emits a
diff --git a/src/tint/lang/spirv/reader/ast_parser/ast_parser_test.cc b/src/tint/lang/spirv/reader/ast_parser/ast_parser_test.cc
index 89758c5..3aaf1f9 100644
--- a/src/tint/lang/spirv/reader/ast_parser/ast_parser_test.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/ast_parser_test.cc
@@ -27,7 +27,9 @@
#include "gmock/gmock.h"
#include "src/tint/lang/spirv/reader/ast_parser/helper_test.h"
+#include "src/tint/lang/spirv/reader/ast_parser/parse.h"
#include "src/tint/lang/spirv/reader/ast_parser/spirv_tools_helpers_test.h"
+#include "src/tint/lang/wgsl/writer/writer.h"
namespace tint::spirv::reader::ast_parser {
namespace {
@@ -260,5 +262,115 @@
EXPECT_THAT(p->error(), HasSubstr("value cannot be represented as 'f32': -inf"));
}
+TEST_F(SpirvASTParserTest, BlendSrc) {
+ auto spv = test::Assemble(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
+)");
+ auto program = Parse(spv, {});
+ auto errs = program.Diagnostics().Str();
+ EXPECT_TRUE(program.IsValid()) << errs;
+ EXPECT_EQ(program.Diagnostics().Count(), 0u) << errs;
+ auto result = wgsl::writer::Generate(program, {});
+ EXPECT_EQ(result, Success);
+ EXPECT_EQ("\n" + result->wgsl, R"(
+enable dual_source_blending;
+
+struct FragOutput {
+ /* @offset(0) */
+ color : vec4f,
+ /* @offset(16) */
+ blend : vec4f,
+}
+
+var<private> frag_main_loc0_idx0_Output : vec4f;
+
+var<private> frag_main_loc0_idx1_Output : vec4f;
+
+const x_17 = vec4f(0.5f, 0.5f, 0.5f, 1.0f);
+
+fn frag_main_inner() -> FragOutput {
+ var output = FragOutput(vec4f(), vec4f());
+ output.color = x_17;
+ output.blend = x_17;
+ let x_22 = output;
+ return x_22;
+}
+
+fn frag_main_1() {
+ let x_27 = frag_main_inner();
+ frag_main_loc0_idx0_Output = x_27.color;
+ frag_main_loc0_idx1_Output = x_27.blend;
+ return;
+}
+
+struct frag_main_out {
+ @location(0) @blend_src(0)
+ frag_main_loc0_idx0_Output_1 : vec4f,
+ @location(0) @blend_src(1)
+ frag_main_loc0_idx1_Output_1 : vec4f,
+}
+
+@fragment
+fn frag_main() -> frag_main_out {
+ frag_main_1();
+ return frag_main_out(frag_main_loc0_idx0_Output, frag_main_loc0_idx1_Output);
+}
+)");
+}
+
} // namespace
} // namespace tint::spirv::reader::ast_parser
diff --git a/src/tint/lang/spirv/reader/ast_parser/parse.cc b/src/tint/lang/spirv/reader/ast_parser/parse.cc
index 9106cbb..8081c14 100644
--- a/src/tint/lang/spirv/reader/ast_parser/parse.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/parse.cc
@@ -99,6 +99,9 @@
allowed_features.extensions.insert(wgsl::Extension::kChromiumDisableUniformityAnalysis);
builder.Enable(wgsl::Extension::kChromiumDisableUniformityAnalysis);
+ // Allow below WGSL extensions unconditionally but not enable them by default.
+ allowed_features.extensions.insert(wgsl::Extension::kDualSourceBlending);
+
// The SPIR-V parser can construct disjoint AST nodes, which is invalid for
// the Resolver. Clone the Program to clean these up.
Program program_with_disjoint_ast(std::move(builder));
diff --git a/test/tint/bug/tint/1520.spvasm b/test/tint/bug/tint/1520.spvasm
index a9071c2..14811c3 100644
--- a/test/tint/bug/tint/1520.spvasm
+++ b/test/tint/bug/tint/1520.spvasm
@@ -35,7 +35,6 @@
OpDecorate %4 DescriptorSet 0
OpDecorate %sk_FragColor RelaxedPrecision
OpDecorate %sk_FragColor Location 0
-OpDecorate %sk_FragColor Index 0
OpDecorate %sk_Clockwise BuiltIn FrontFacing
OpDecorate %vcolor_S0 RelaxedPrecision
OpDecorate %vcolor_S0 Location 0