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>
This commit is contained in:
parent
1f2d38c826
commit
a2fa690cda
|
@ -747,6 +747,9 @@ FunctionEmitter::FunctionEmitter(ParserImpl* pi,
|
|||
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 @@ TypedExpression FunctionEmitter::MakeExpression(uint32_t id) {
|
|||
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 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
|
|||
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 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
|
|||
}
|
||||
|
||||
// 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 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
|
|||
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 FunctionEmitter::MakeCompositeExtract(
|
|||
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 @@ bool FunctionEmitter::RegisterSpecialBuiltInVariables() {
|
|||
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 @@ bool FunctionEmitter::RegisterSpecialBuiltInVariables() {
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -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 @@ enum class SkipReason {
|
|||
/// `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 @@ inline std::ostream& operator<<(std::ostream& o, const DefInfo& di) {
|
|||
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 @@ class FunctionEmitter {
|
|||
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
|
||||
|
|
|
@ -1231,6 +1231,31 @@ bool ParserImpl::EmitModuleScopeVariables() {
|
|||
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 @@ ast::Variable* ParserImpl::MakeVariable(
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -287,6 +287,11 @@ class ParserImpl : Reader {
|
|||
/// @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
|
||||
|
|
|
@ -2403,6 +2403,571 @@ TEST_F(SpvModuleScopeVarParserTest, SampleId_U32_FunctParam) {
|
|||
})")) << 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
|
||||
|
|
Loading…
Reference in New Issue