spirv-reader: sample_mask_in, sample_mask_out
TODO: passing pointer to them as a function parameter
Bug: tint:471
Change-Id: Ibd55bdc77a2bfb0f5712dd9bf332910999b8d0d1
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/40123
Commit-Queue: David Neto <dneto@google.com>
Auto-Submit: David Neto <dneto@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 2a55512..41ac1a4 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -747,6 +747,9 @@
namer_(pi->namer()),
function_(function),
i32_(builder_.create<type::I32>()),
+ u32_(builder_.create<type::U32>()),
+ sample_mask_in_id(0u),
+ sample_mask_out_id(0u),
ep_info_(ep_info) {
PushNewStatementBlock(nullptr, 0, nullptr);
}
@@ -2032,6 +2035,17 @@
Fail() << "unhandled use of a pointer to the SampleId builtin, with ID: "
<< id;
return {};
+ case SkipReason::kSampleMaskInBuiltinPointer:
+ Fail()
+ << "unhandled use of a pointer to the SampleMask builtin, with ID: "
+ << id;
+ return {};
+ case SkipReason::kSampleMaskOutBuiltinPointer:
+ // The result type is always u32.
+ auto name = namer_.Name(sample_mask_out_id);
+ return TypedExpression{u32_,
+ create<ast::IdentifierExpression>(
+ Source{}, builder_.Symbols().Register(name))};
}
if (identifier_values_.count(id) || parser_impl_.IsScalarSpecConstant(id)) {
auto name = namer_.Name(id);
@@ -2968,25 +2982,41 @@
return true;
case SpvOpStore: {
- const auto ptr_id = inst.GetSingleWordInOperand(0);
+ auto ptr_id = inst.GetSingleWordInOperand(0);
const auto value_id = inst.GetSingleWordInOperand(1);
+ auto rhs = MakeExpression(value_id);
+
// Handle exceptional cases
- if (GetSkipReason(ptr_id) == SkipReason::kPointSizeBuiltinPointer) {
- if (const auto* c = constant_mgr_->FindDeclaredConstant(value_id)) {
- // If we're writing a constant 1.0, then skip the write. That's all
- // that WebGPU handles.
- auto* ct = c->type();
- if (ct->AsFloat() && (ct->AsFloat()->width() == 32) &&
- (c->GetFloat() == 1.0f)) {
- // Don't store to PointSize
- return true;
+ switch (GetSkipReason(ptr_id)) {
+ case SkipReason::kPointSizeBuiltinPointer:
+ if (const auto* c = constant_mgr_->FindDeclaredConstant(value_id)) {
+ // If we're writing a constant 1.0, then skip the write. That's all
+ // that WebGPU handles.
+ auto* ct = c->type();
+ if (ct->AsFloat() && (ct->AsFloat()->width() == 32) &&
+ (c->GetFloat() == 1.0f)) {
+ // Don't store to PointSize
+ return true;
+ }
}
- }
- return Fail() << "cannot store a value other than constant 1.0 to "
- "PointSize builtin: "
- << inst.PrettyPrint();
+ return Fail() << "cannot store a value other than constant 1.0 to "
+ "PointSize builtin: "
+ << inst.PrettyPrint();
+
+ case SkipReason::kSampleMaskOutBuiltinPointer:
+ ptr_id = sample_mask_out_id;
+ if (rhs.type != u32_) {
+ // WGSL requires sample_mask_out to be signed.
+ rhs = TypedExpression{
+ u32_, create<ast::TypeConstructorExpression>(
+ Source{}, u32_, ast::ExpressionList{rhs.expr})};
+ }
+ break;
+ default:
+ break;
}
+
const auto ptr_type_id = def_use_mgr_->GetDef(ptr_id)->type_id();
const auto& builtin_position_info = parser_impl_.GetBuiltInPositionInfo();
if (ptr_type_id == builtin_position_info.pointer_type_id) {
@@ -2996,9 +3026,7 @@
}
// Handle an ordinary store as an assignment.
- // TODO(dneto): Order of evaluation?
auto lhs = MakeExpression(ptr_id);
- auto rhs = MakeExpression(value_id);
AddStatement(
create<ast::AssignmentStatement>(Source{}, lhs.expr, rhs.expr));
return success();
@@ -3024,6 +3052,25 @@
Source{}, i32_, ast::ExpressionList{id_expr})};
return EmitConstDefinition(inst, expr);
}
+ case SkipReason::kSampleMaskInBuiltinPointer: {
+ auto name = namer_.Name(sample_mask_in_id);
+ ast::Expression* id_expr = create<ast::IdentifierExpression>(
+ Source{}, builder_.Symbols().Register(name));
+ auto* load_result_type = parser_impl_.ConvertType(inst.type_id());
+ ast::Expression* ast_expr = nullptr;
+ if (load_result_type == i32_) {
+ ast_expr = create<ast::TypeConstructorExpression>(
+ Source{}, i32_, ast::ExpressionList{id_expr});
+ } else if (load_result_type == u32_) {
+ ast_expr = id_expr;
+ } else {
+ return Fail() << "loading the whole SampleMask input array is not "
+ "supported: "
+ << inst.PrettyPrint();
+ }
+ return EmitConstDefinition(
+ inst, TypedExpression{load_result_type, ast_expr});
+ }
default:
break;
}
@@ -3495,9 +3542,8 @@
TypedExpression current_expr(MakeOperand(inst, 0));
auto make_index = [this, source](uint32_t literal) {
- auto* type = create<type::U32>();
return create<ast::ScalarConstructorExpression>(
- source, create<ast::UintLiteral>(source, type, literal));
+ source, create<ast::UintLiteral>(source, u32_, literal));
};
const auto composite = inst.GetSingleWordInOperand(0);
@@ -3655,8 +3701,8 @@
for (auto& special_var : parser_impl_.special_builtins()) {
const auto id = special_var.first;
const auto builtin = special_var.second;
- def_info_[id] =
- std::make_unique<DefInfo>(*(def_use_mgr_->GetDef(id)), 0, index);
+ const auto* var = def_use_mgr_->GetDef(id);
+ def_info_[id] = std::make_unique<DefInfo>(*var, 0, index);
++index;
auto& def = def_info_[id];
switch (builtin) {
@@ -3666,6 +3712,19 @@
case SpvBuiltInSampleId:
def->skip = SkipReason::kSampleIdBuiltinPointer;
break;
+ case SpvBuiltInSampleMask: {
+ // Distinguish between input and output variable.
+ const auto storage_class =
+ static_cast<SpvStorageClass>(var->GetSingleWordInOperand(0));
+ if (storage_class == SpvStorageClassInput) {
+ sample_mask_in_id = id;
+ def->skip = SkipReason::kSampleMaskInBuiltinPointer;
+ } else {
+ sample_mask_out_id = id;
+ def->skip = SkipReason::kSampleMaskOutBuiltinPointer;
+ }
+ break;
+ }
default:
return Fail() << "unrecognized special builtin: " << int(builtin);
}
diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h
index 6f1ca66..d58c66f 100644
--- a/src/reader/spirv/function.h
+++ b/src/reader/spirv/function.h
@@ -44,6 +44,7 @@
#include "src/reader/spirv/parser_impl.h"
#include "src/type/i32_type.h"
#include "src/type/texture_type.h"
+#include "src/type/u32_type.h"
namespace tint {
namespace reader {
@@ -227,6 +228,14 @@
/// `kSampleIdBuiltinPointer`: the value is a pointer to the SampleId builtin
/// variable. Don't generate its address.
kSampleIdBuiltinPointer,
+
+ /// `kSampleMaskInBuiltinPointer`: the value is a pointer to the SampleMaskIn
+ /// builtin input variable. Don't generate its address.
+ kSampleMaskInBuiltinPointer,
+
+ /// `kSampleMaskOutBuiltinPointer`: the value is a pointer to the SampleMask
+ /// builtin output variable.
+ kSampleMaskOutBuiltinPointer,
};
/// Bookkeeping info for a SPIR-V ID defined in the function, or some
@@ -335,6 +344,12 @@
case SkipReason::kSampleIdBuiltinPointer:
o << " skip:sampleid_pointer";
break;
+ case SkipReason::kSampleMaskInBuiltinPointer:
+ o << " skip:samplemaskin_pointer";
+ break;
+ case SkipReason::kSampleMaskOutBuiltinPointer:
+ o << " skip:samplemaskout_pointer";
+ break;
}
o << "}";
return o;
@@ -1085,6 +1100,12 @@
Namer& namer_;
const spvtools::opt::Function& function_;
type::I32* const i32_; // The unique I32 type object.
+ type::U32* const u32_; // The unique U32 type object.
+
+ // The SPIR-V ID for the SampleMask input variable.
+ uint32_t sample_mask_in_id;
+ // The SPIR-V ID for the SampleMask output variable.
+ uint32_t sample_mask_out_id;
// A stack of statement lists. Each list is contained in a construct in
// the next deeper element of stack. The 0th entry represents the statements
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index a72b474..8b8556f 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -1231,6 +1231,31 @@
return success_;
}
+// @param var_id SPIR-V id of an OpVariable, assumed to be pointer
+// to an array
+// @returns the IntConstant for the size of the array, or nullptr
+const spvtools::opt::analysis::IntConstant* ParserImpl::GetArraySize(
+ uint32_t var_id) {
+ auto* var = def_use_mgr_->GetDef(var_id);
+ if (!var || var->opcode() != SpvOpVariable) {
+ return nullptr;
+ }
+ auto* ptr_type = def_use_mgr_->GetDef(var->type_id());
+ if (!ptr_type || ptr_type->opcode() != SpvOpTypePointer) {
+ return nullptr;
+ }
+ auto* array_type = def_use_mgr_->GetDef(ptr_type->GetSingleWordInOperand(1));
+ if (!array_type || array_type->opcode() != SpvOpTypeArray) {
+ return nullptr;
+ }
+ auto* size = constant_mgr_->FindDeclaredConstant(
+ array_type->GetSingleWordInOperand(1));
+ if (!size) {
+ return nullptr;
+ }
+ return size->AsIntConstant();
+}
+
ast::Variable* ParserImpl::MakeVariable(
uint32_t id,
ast::StorageClass sc,
@@ -1277,6 +1302,20 @@
type = forced_type;
}
break;
+ case SpvBuiltInSampleMask: {
+ // In SPIR-V this is used for both input and output variable.
+ // The SPIR-V variable has store type of array of integer scalar,
+ // either signed or unsigned.
+ // WGSL requires the store type to be u32.
+ auto* size = GetArraySize(id);
+ if (!size || size->GetZeroExtendedValue() != 1) {
+ Fail() << "WGSL supports a sample mask of at most 32 bits. "
+ "SampleMask must be an array of 1 element.";
+ }
+ special_builtins_[id] = spv_builtin;
+ type = builder_.create<type::U32>();
+ break;
+ }
default:
break;
}
diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h
index 3b14f77..bfe11de 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -287,6 +287,11 @@
/// @returns true if parser is still successful.
bool EmitFunction(const spvtools::opt::Function& f);
+ /// Returns the integer constant for the array size of the given variable.
+ /// @param var_id SPIR-V ID for an array variable
+ /// @returns the integer constant for its array size, or nullptr.
+ const spvtools::opt::analysis::IntConstant* GetArraySize(uint32_t var_id);
+
/// Creates an AST Variable node for a SPIR-V ID, including any attached
/// decorations, unless it's an ignorable builtin variable.
/// @param id the SPIR-V result ID
diff --git a/src/reader/spirv/parser_impl_module_var_test.cc b/src/reader/spirv/parser_impl_module_var_test.cc
index 2fd4ea2..21ed781 100644
--- a/src/reader/spirv/parser_impl_module_var_test.cc
+++ b/src/reader/spirv/parser_impl_module_var_test.cc
@@ -2403,6 +2403,571 @@
})")) << module_str;
}
+// Returns the start of a shader for testing SampleMask
+// parameterized by store type.
+std::string SampleMaskPreamble(std::string store_type) {
+ return R"(
+ OpCapability Shader
+ OpMemoryModel Logical Simple
+ OpEntryPoint Fragment %main "main" %1
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %1 BuiltIn SampleMask
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+ %int = OpTypeInt 32 1
+ %int_12 = OpConstant %int 12
+ %uint_0 = OpConstant %uint 0
+ %uint_1 = OpConstant %uint 1
+ %uint_2 = OpConstant %uint 2
+ %uarr1 = OpTypeArray %uint %uint_1
+ %uarr2 = OpTypeArray %uint %uint_2
+ %iarr1 = OpTypeArray %int %uint_1
+ %iarr2 = OpTypeArray %int %uint_2
+ %iptr_in_ty = OpTypePointer Input %int
+ %uptr_in_ty = OpTypePointer Input %uint
+ %iptr_out_ty = OpTypePointer Output %int
+ %uptr_out_ty = OpTypePointer Output %uint
+ %in_ty = OpTypePointer Input )" +
+ store_type + R"(
+ %out_ty = OpTypePointer Output )" +
+ store_type + R"(
+)";
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_ArraySize2_Error) {
+ const std::string assembly = SampleMaskPreamble("%uarr2") + R"(
+ %1 = OpVariable %in_ty Input
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %uptr_in_ty %1 %uint_0
+ %3 = OpLoad %int %2
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_FALSE(p->BuildAndParseInternalModule());
+ EXPECT_THAT(p->error(),
+ HasSubstr("WGSL supports a sample mask of at most 32 bits. "
+ "SampleMask must be an array of 1 element"))
+ << p->error() << assembly;
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_U32_Direct) {
+ const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
+ %1 = OpVariable %in_ty Input
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %uptr_in_ty %1 %uint_0
+ %3 = OpLoad %uint %2
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+ EXPECT_TRUE(p->error().empty());
+ const auto module_str = p->program().to_str();
+ // Correct declaration
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Variable{
+ Decorations{
+ BuiltinDecoration{sample_mask_in}
+ }
+ x_1
+ in
+ __u32
+ })"));
+
+ // Correct bodies
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ VariableDeclStatement{
+ VariableConst{
+ x_3
+ none
+ __u32
+ {
+ Identifier[not set]{x_1}
+ }
+ }
+ })"))
+ << module_str;
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_U32_CopyObject) {
+ const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
+ %1 = OpVariable %in_ty Input
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %uptr_in_ty %1 %uint_0
+ %3 = OpCopyObject %uptr_in_ty %2
+ %4 = OpLoad %uint %3
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+ EXPECT_TRUE(p->error().empty());
+ const auto module_str = p->program().to_str();
+ // Correct declaration
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Variable{
+ Decorations{
+ BuiltinDecoration{sample_mask_in}
+ }
+ x_1
+ in
+ __u32
+ })"));
+
+ // Correct bodies
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ VariableDeclStatement{
+ VariableConst{
+ x_4
+ none
+ __u32
+ {
+ Identifier[not set]{x_1}
+ }
+ }
+ })"))
+ << module_str;
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_U32_AccessChain) {
+ const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
+ %1 = OpVariable %in_ty Input
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %uptr_in_ty %1 %uint_0
+ %3 = OpAccessChain %uptr_in_ty %2
+ %4 = OpLoad %uint %3
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+ EXPECT_TRUE(p->error().empty());
+ const auto module_str = p->program().to_str();
+ // Correct declaration
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Variable{
+ Decorations{
+ BuiltinDecoration{sample_mask_in}
+ }
+ x_1
+ in
+ __u32
+ })"));
+
+ // Correct bodies
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ VariableDeclStatement{
+ VariableConst{
+ x_4
+ none
+ __u32
+ {
+ Identifier[not set]{x_1}
+ }
+ }
+ })"))
+ << module_str;
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_I32_Direct) {
+ const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
+ %1 = OpVariable %in_ty Input
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %iptr_in_ty %1 %uint_0
+ %3 = OpLoad %int %2
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+ EXPECT_TRUE(p->error().empty());
+ const auto module_str = p->program().to_str();
+ // Correct declaration
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Variable{
+ Decorations{
+ BuiltinDecoration{sample_mask_in}
+ }
+ x_1
+ in
+ __u32
+ })"));
+
+ // Correct bodies
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ VariableDeclStatement{
+ VariableConst{
+ x_3
+ none
+ __i32
+ {
+ TypeConstructor[not set]{
+ __i32
+ Identifier[not set]{x_1}
+ }
+ }
+ }
+ })"))
+ << module_str;
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_I32_CopyObject) {
+ const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
+ %1 = OpVariable %in_ty Input
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %iptr_in_ty %1 %uint_0
+ %3 = OpCopyObject %iptr_in_ty %2
+ %4 = OpLoad %int %3
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+ EXPECT_TRUE(p->error().empty());
+ const auto module_str = p->program().to_str();
+ // Correct declaration
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Variable{
+ Decorations{
+ BuiltinDecoration{sample_mask_in}
+ }
+ x_1
+ in
+ __u32
+ })"));
+
+ // Correct bodies
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ VariableDeclStatement{
+ VariableConst{
+ x_4
+ none
+ __i32
+ {
+ TypeConstructor[not set]{
+ __i32
+ Identifier[not set]{x_1}
+ }
+ }
+ }
+ })"))
+ << module_str;
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_I32_AccessChain) {
+ const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
+ %1 = OpVariable %in_ty Input
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %iptr_in_ty %1 %uint_0
+ %3 = OpAccessChain %iptr_in_ty %2
+ %4 = OpLoad %int %3
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+ EXPECT_TRUE(p->error().empty());
+ const auto module_str = p->program().to_str();
+ // Correct declaration
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Variable{
+ Decorations{
+ BuiltinDecoration{sample_mask_in}
+ }
+ x_1
+ in
+ __u32
+ })"));
+
+ // Correct bodies
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ VariableDeclStatement{
+ VariableConst{
+ x_4
+ none
+ __i32
+ {
+ TypeConstructor[not set]{
+ __i32
+ Identifier[not set]{x_1}
+ }
+ }
+ }
+ })"))
+ << module_str;
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_ArraySize2_Error) {
+ const std::string assembly = SampleMaskPreamble("%uarr2") + R"(
+ %1 = OpVariable %out_ty Output
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %uptr_out_ty %1 %uint_0
+ OpStore %2 %uint_0
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_FALSE(p->BuildAndParseInternalModule());
+ EXPECT_THAT(p->error(),
+ HasSubstr("WGSL supports a sample mask of at most 32 bits. "
+ "SampleMask must be an array of 1 element"))
+ << p->error() << assembly;
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_Direct) {
+ const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
+ %1 = OpVariable %out_ty Output
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %uptr_out_ty %1 %uint_0
+ OpStore %2 %uint_0
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+ EXPECT_TRUE(p->error().empty());
+ const auto module_str = p->program().to_str();
+ // Correct declaration
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Variable{
+ Decorations{
+ BuiltinDecoration{sample_mask_out}
+ }
+ x_1
+ out
+ __u32
+ })"));
+
+ // Correct bodies
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Assignment{
+ Identifier[not set]{x_1}
+ ScalarConstructor[not set]{0}
+ })"))
+ << module_str;
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_CopyObject) {
+ const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
+ %1 = OpVariable %out_ty Output
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %uptr_out_ty %1 %uint_0
+ %3 = OpCopyObject %uptr_out_ty %2
+ OpStore %2 %uint_0
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+ EXPECT_TRUE(p->error().empty());
+ const auto module_str = p->program().to_str();
+ // Correct declaration
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Variable{
+ Decorations{
+ BuiltinDecoration{sample_mask_out}
+ }
+ x_1
+ out
+ __u32
+ })"));
+
+ // Correct bodies
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Assignment{
+ Identifier[not set]{x_1}
+ ScalarConstructor[not set]{0}
+ })"))
+ << module_str;
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_AccessChain) {
+ const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
+ %1 = OpVariable %out_ty Output
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %uptr_out_ty %1 %uint_0
+ %3 = OpAccessChain %uptr_out_ty %2
+ OpStore %2 %uint_0
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+ EXPECT_TRUE(p->error().empty());
+ const auto module_str = p->program().to_str();
+ // Correct declaration
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Variable{
+ Decorations{
+ BuiltinDecoration{sample_mask_out}
+ }
+ x_1
+ out
+ __u32
+ })"));
+
+ // Correct bodies
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Assignment{
+ Identifier[not set]{x_1}
+ ScalarConstructor[not set]{0}
+ })"))
+ << module_str;
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_Direct) {
+ const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
+ %1 = OpVariable %out_ty Output
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %iptr_out_ty %1 %uint_0
+ OpStore %2 %int_12
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+ EXPECT_TRUE(p->error().empty());
+ const auto module_str = p->program().to_str();
+ // Correct declaration
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Variable{
+ Decorations{
+ BuiltinDecoration{sample_mask_out}
+ }
+ x_1
+ out
+ __u32
+ })"));
+
+ // Correct bodies
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ {
+ Assignment{
+ Identifier[not set]{x_1}
+ TypeConstructor[not set]{
+ __u32
+ ScalarConstructor[not set]{12}
+ }
+ }
+ Return{}
+ })"))
+ << module_str;
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_CopyObject) {
+ const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
+ %1 = OpVariable %out_ty Output
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %iptr_out_ty %1 %uint_0
+ %3 = OpCopyObject %iptr_out_ty %2
+ OpStore %2 %int_12
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+ EXPECT_TRUE(p->error().empty());
+ const auto module_str = p->program().to_str();
+ // Correct declaration
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Variable{
+ Decorations{
+ BuiltinDecoration{sample_mask_out}
+ }
+ x_1
+ out
+ __u32
+ })"));
+
+ // Correct bodies
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ {
+ Assignment{
+ Identifier[not set]{x_1}
+ TypeConstructor[not set]{
+ __u32
+ ScalarConstructor[not set]{12}
+ }
+ }
+ Return{}
+ })"))
+ << module_str;
+}
+
+TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_AccessChain) {
+ const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
+ %1 = OpVariable %out_ty Output
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %2 = OpAccessChain %iptr_out_ty %1 %uint_0
+ %3 = OpAccessChain %iptr_out_ty %2
+ OpStore %2 %int_12
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+ EXPECT_TRUE(p->error().empty());
+ const auto module_str = p->program().to_str();
+ // Correct declaration
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ Variable{
+ Decorations{
+ BuiltinDecoration{sample_mask_out}
+ }
+ x_1
+ out
+ __u32
+ })"));
+
+ // Correct bodies
+ EXPECT_THAT(module_str, HasSubstr(R"(
+ {
+ Assignment{
+ Identifier[not set]{x_1}
+ TypeConstructor[not set]{
+ __u32
+ ScalarConstructor[not set]{12}
+ }
+ }
+ Return{}
+ })"))
+ << module_str;
+}
+
+// TODO(dneto): Test passing pointer to SampleMask as function parameter,
+// both input case and output case.
+
} // namespace
} // namespace spirv
} // namespace reader