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()),
|
namer_(pi->namer()),
|
||||||
function_(function),
|
function_(function),
|
||||||
i32_(builder_.create<type::I32>()),
|
i32_(builder_.create<type::I32>()),
|
||||||
|
u32_(builder_.create<type::U32>()),
|
||||||
|
sample_mask_in_id(0u),
|
||||||
|
sample_mask_out_id(0u),
|
||||||
ep_info_(ep_info) {
|
ep_info_(ep_info) {
|
||||||
PushNewStatementBlock(nullptr, 0, nullptr);
|
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: "
|
Fail() << "unhandled use of a pointer to the SampleId builtin, with ID: "
|
||||||
<< id;
|
<< id;
|
||||||
return {};
|
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)) {
|
if (identifier_values_.count(id) || parser_impl_.IsScalarSpecConstant(id)) {
|
||||||
auto name = namer_.Name(id);
|
auto name = namer_.Name(id);
|
||||||
|
@ -2968,25 +2982,41 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case SpvOpStore: {
|
case SpvOpStore: {
|
||||||
const auto ptr_id = inst.GetSingleWordInOperand(0);
|
auto ptr_id = inst.GetSingleWordInOperand(0);
|
||||||
const auto value_id = inst.GetSingleWordInOperand(1);
|
const auto value_id = inst.GetSingleWordInOperand(1);
|
||||||
|
|
||||||
|
auto rhs = MakeExpression(value_id);
|
||||||
|
|
||||||
// Handle exceptional cases
|
// Handle exceptional cases
|
||||||
if (GetSkipReason(ptr_id) == SkipReason::kPointSizeBuiltinPointer) {
|
switch (GetSkipReason(ptr_id)) {
|
||||||
if (const auto* c = constant_mgr_->FindDeclaredConstant(value_id)) {
|
case SkipReason::kPointSizeBuiltinPointer:
|
||||||
// If we're writing a constant 1.0, then skip the write. That's all
|
if (const auto* c = constant_mgr_->FindDeclaredConstant(value_id)) {
|
||||||
// that WebGPU handles.
|
// If we're writing a constant 1.0, then skip the write. That's all
|
||||||
auto* ct = c->type();
|
// that WebGPU handles.
|
||||||
if (ct->AsFloat() && (ct->AsFloat()->width() == 32) &&
|
auto* ct = c->type();
|
||||||
(c->GetFloat() == 1.0f)) {
|
if (ct->AsFloat() && (ct->AsFloat()->width() == 32) &&
|
||||||
// Don't store to PointSize
|
(c->GetFloat() == 1.0f)) {
|
||||||
return true;
|
// Don't store to PointSize
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return Fail() << "cannot store a value other than constant 1.0 to "
|
||||||
return Fail() << "cannot store a value other than constant 1.0 to "
|
"PointSize builtin: "
|
||||||
"PointSize builtin: "
|
<< inst.PrettyPrint();
|
||||||
<< 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 ptr_type_id = def_use_mgr_->GetDef(ptr_id)->type_id();
|
||||||
const auto& builtin_position_info = parser_impl_.GetBuiltInPositionInfo();
|
const auto& builtin_position_info = parser_impl_.GetBuiltInPositionInfo();
|
||||||
if (ptr_type_id == builtin_position_info.pointer_type_id) {
|
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.
|
// Handle an ordinary store as an assignment.
|
||||||
// TODO(dneto): Order of evaluation?
|
|
||||||
auto lhs = MakeExpression(ptr_id);
|
auto lhs = MakeExpression(ptr_id);
|
||||||
auto rhs = MakeExpression(value_id);
|
|
||||||
AddStatement(
|
AddStatement(
|
||||||
create<ast::AssignmentStatement>(Source{}, lhs.expr, rhs.expr));
|
create<ast::AssignmentStatement>(Source{}, lhs.expr, rhs.expr));
|
||||||
return success();
|
return success();
|
||||||
|
@ -3024,6 +3052,25 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
|
||||||
Source{}, i32_, ast::ExpressionList{id_expr})};
|
Source{}, i32_, ast::ExpressionList{id_expr})};
|
||||||
return EmitConstDefinition(inst, 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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3495,9 +3542,8 @@ TypedExpression FunctionEmitter::MakeCompositeExtract(
|
||||||
TypedExpression current_expr(MakeOperand(inst, 0));
|
TypedExpression current_expr(MakeOperand(inst, 0));
|
||||||
|
|
||||||
auto make_index = [this, source](uint32_t literal) {
|
auto make_index = [this, source](uint32_t literal) {
|
||||||
auto* type = create<type::U32>();
|
|
||||||
return create<ast::ScalarConstructorExpression>(
|
return create<ast::ScalarConstructorExpression>(
|
||||||
source, create<ast::UintLiteral>(source, type, literal));
|
source, create<ast::UintLiteral>(source, u32_, literal));
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto composite = inst.GetSingleWordInOperand(0);
|
const auto composite = inst.GetSingleWordInOperand(0);
|
||||||
|
@ -3655,8 +3701,8 @@ bool FunctionEmitter::RegisterSpecialBuiltInVariables() {
|
||||||
for (auto& special_var : parser_impl_.special_builtins()) {
|
for (auto& special_var : parser_impl_.special_builtins()) {
|
||||||
const auto id = special_var.first;
|
const auto id = special_var.first;
|
||||||
const auto builtin = special_var.second;
|
const auto builtin = special_var.second;
|
||||||
def_info_[id] =
|
const auto* var = def_use_mgr_->GetDef(id);
|
||||||
std::make_unique<DefInfo>(*(def_use_mgr_->GetDef(id)), 0, index);
|
def_info_[id] = std::make_unique<DefInfo>(*var, 0, index);
|
||||||
++index;
|
++index;
|
||||||
auto& def = def_info_[id];
|
auto& def = def_info_[id];
|
||||||
switch (builtin) {
|
switch (builtin) {
|
||||||
|
@ -3666,6 +3712,19 @@ bool FunctionEmitter::RegisterSpecialBuiltInVariables() {
|
||||||
case SpvBuiltInSampleId:
|
case SpvBuiltInSampleId:
|
||||||
def->skip = SkipReason::kSampleIdBuiltinPointer;
|
def->skip = SkipReason::kSampleIdBuiltinPointer;
|
||||||
break;
|
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:
|
default:
|
||||||
return Fail() << "unrecognized special builtin: " << int(builtin);
|
return Fail() << "unrecognized special builtin: " << int(builtin);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "src/reader/spirv/parser_impl.h"
|
#include "src/reader/spirv/parser_impl.h"
|
||||||
#include "src/type/i32_type.h"
|
#include "src/type/i32_type.h"
|
||||||
#include "src/type/texture_type.h"
|
#include "src/type/texture_type.h"
|
||||||
|
#include "src/type/u32_type.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace reader {
|
namespace reader {
|
||||||
|
@ -227,6 +228,14 @@ enum class SkipReason {
|
||||||
/// `kSampleIdBuiltinPointer`: the value is a pointer to the SampleId builtin
|
/// `kSampleIdBuiltinPointer`: the value is a pointer to the SampleId builtin
|
||||||
/// variable. Don't generate its address.
|
/// variable. Don't generate its address.
|
||||||
kSampleIdBuiltinPointer,
|
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
|
/// 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:
|
case SkipReason::kSampleIdBuiltinPointer:
|
||||||
o << " skip:sampleid_pointer";
|
o << " skip:sampleid_pointer";
|
||||||
break;
|
break;
|
||||||
|
case SkipReason::kSampleMaskInBuiltinPointer:
|
||||||
|
o << " skip:samplemaskin_pointer";
|
||||||
|
break;
|
||||||
|
case SkipReason::kSampleMaskOutBuiltinPointer:
|
||||||
|
o << " skip:samplemaskout_pointer";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
o << "}";
|
o << "}";
|
||||||
return o;
|
return o;
|
||||||
|
@ -1085,6 +1100,12 @@ class FunctionEmitter {
|
||||||
Namer& namer_;
|
Namer& namer_;
|
||||||
const spvtools::opt::Function& function_;
|
const spvtools::opt::Function& function_;
|
||||||
type::I32* const i32_; // The unique I32 type object.
|
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
|
// 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
|
// the next deeper element of stack. The 0th entry represents the statements
|
||||||
|
|
|
@ -1231,6 +1231,31 @@ bool ParserImpl::EmitModuleScopeVariables() {
|
||||||
return success_;
|
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(
|
ast::Variable* ParserImpl::MakeVariable(
|
||||||
uint32_t id,
|
uint32_t id,
|
||||||
ast::StorageClass sc,
|
ast::StorageClass sc,
|
||||||
|
@ -1277,6 +1302,20 @@ ast::Variable* ParserImpl::MakeVariable(
|
||||||
type = forced_type;
|
type = forced_type;
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,6 +287,11 @@ class ParserImpl : Reader {
|
||||||
/// @returns true if parser is still successful.
|
/// @returns true if parser is still successful.
|
||||||
bool EmitFunction(const spvtools::opt::Function& f);
|
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
|
/// Creates an AST Variable node for a SPIR-V ID, including any attached
|
||||||
/// decorations, unless it's an ignorable builtin variable.
|
/// decorations, unless it's an ignorable builtin variable.
|
||||||
/// @param id the SPIR-V result ID
|
/// @param id the SPIR-V result ID
|
||||||
|
|
|
@ -2403,6 +2403,571 @@ TEST_F(SpvModuleScopeVarParserTest, SampleId_U32_FunctParam) {
|
||||||
})")) << module_str;
|
})")) << 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
|
||||||
} // namespace spirv
|
} // namespace spirv
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
|
|
Loading…
Reference in New Issue