spirv-reader: flatten output matrix, array, struct
Bug: tint:912 Change-Id: Iebbcb7ea8d870cccadad7dd1ce8aaccf8965b370 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/56301 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: James Price <jrprice@google.com> Auto-Submit: David Neto <dneto@google.com>
This commit is contained in:
parent
c15dbfa1ae
commit
91622e3853
|
@ -935,14 +935,15 @@ ast::BlockStatement* FunctionEmitter::MakeFunctionBody() {
|
|||
return body;
|
||||
}
|
||||
|
||||
bool FunctionEmitter::EmitInputParameter(std::string var_name,
|
||||
const Type* var_type,
|
||||
ast::DecorationList* decos,
|
||||
std::vector<int> index_prefix,
|
||||
const Type* tip_type,
|
||||
const Type* forced_param_type,
|
||||
ast::VariableList* params,
|
||||
ast::StatementList* statements) {
|
||||
bool FunctionEmitter::EmitPipelineInput(std::string var_name,
|
||||
const Type* var_type,
|
||||
ast::DecorationList* decos,
|
||||
std::vector<int> index_prefix,
|
||||
const Type* tip_type,
|
||||
const Type* forced_param_type,
|
||||
ast::VariableList* params,
|
||||
ast::StatementList* statements) {
|
||||
// TODO(dneto): Handle structs where the locations are annotated on members.
|
||||
tip_type = tip_type->UnwrapAlias();
|
||||
if (auto* ref_type = tip_type->As<Reference>()) {
|
||||
tip_type = ref_type->type;
|
||||
|
@ -954,8 +955,8 @@ bool FunctionEmitter::EmitInputParameter(std::string var_name,
|
|||
const Type* vec_ty = ty_.Vector(matrix_type->type, matrix_type->rows);
|
||||
for (int col = 0; col < num_columns; col++) {
|
||||
index_prefix.back() = col;
|
||||
if (!EmitInputParameter(var_name, var_type, decos, index_prefix, vec_ty,
|
||||
forced_param_type, params, statements)) {
|
||||
if (!EmitPipelineInput(var_name, var_type, decos, index_prefix, vec_ty,
|
||||
forced_param_type, params, statements)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -968,8 +969,8 @@ bool FunctionEmitter::EmitInputParameter(std::string var_name,
|
|||
const Type* elem_ty = array_type->type;
|
||||
for (int i = 0; i < static_cast<int>(array_type->size); i++) {
|
||||
index_prefix.back() = i;
|
||||
if (!EmitInputParameter(var_name, var_type, decos, index_prefix, elem_ty,
|
||||
forced_param_type, params, statements)) {
|
||||
if (!EmitPipelineInput(var_name, var_type, decos, index_prefix, elem_ty,
|
||||
forced_param_type, params, statements)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -979,9 +980,9 @@ bool FunctionEmitter::EmitInputParameter(std::string var_name,
|
|||
index_prefix.push_back(0);
|
||||
for (int i = 0; i < static_cast<int>(members.size()); ++i) {
|
||||
index_prefix.back() = i;
|
||||
if (!EmitInputParameter(var_name, var_type, decos, index_prefix,
|
||||
members[i], forced_param_type, params,
|
||||
statements)) {
|
||||
if (!EmitPipelineInput(var_name, var_type, decos, index_prefix,
|
||||
members[i], forced_param_type, params,
|
||||
statements)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1047,6 +1048,120 @@ bool FunctionEmitter::EmitInputParameter(std::string var_name,
|
|||
return success();
|
||||
}
|
||||
|
||||
bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
|
||||
const Type* var_type,
|
||||
ast::DecorationList* decos,
|
||||
std::vector<int> index_prefix,
|
||||
const Type* tip_type,
|
||||
const Type* forced_member_type,
|
||||
ast::StructMemberList* return_members,
|
||||
ast::ExpressionList* return_exprs) {
|
||||
// TODO(dneto): Handle structs where the locations are annotated on members.
|
||||
tip_type = tip_type->UnwrapAlias();
|
||||
if (auto* ref_type = tip_type->As<Reference>()) {
|
||||
tip_type = ref_type->type;
|
||||
}
|
||||
|
||||
if (auto* matrix_type = tip_type->As<Matrix>()) {
|
||||
index_prefix.push_back(0);
|
||||
const auto num_columns = static_cast<int>(matrix_type->columns);
|
||||
const Type* vec_ty = ty_.Vector(matrix_type->type, matrix_type->rows);
|
||||
for (int col = 0; col < num_columns; col++) {
|
||||
index_prefix.back() = col;
|
||||
if (!EmitPipelineOutput(var_name, var_type, decos, index_prefix, vec_ty,
|
||||
forced_member_type, return_members,
|
||||
return_exprs)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return success();
|
||||
} else if (auto* array_type = tip_type->As<Array>()) {
|
||||
if (array_type->size == 0) {
|
||||
return Fail() << "runtime-size array not allowed on pipeline IO";
|
||||
}
|
||||
index_prefix.push_back(0);
|
||||
const Type* elem_ty = array_type->type;
|
||||
for (int i = 0; i < static_cast<int>(array_type->size); i++) {
|
||||
index_prefix.back() = i;
|
||||
if (!EmitPipelineOutput(var_name, var_type, decos, index_prefix, elem_ty,
|
||||
forced_member_type, return_members,
|
||||
return_exprs)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return success();
|
||||
} else if (auto* struct_type = tip_type->As<Struct>()) {
|
||||
const auto& members = struct_type->members;
|
||||
index_prefix.push_back(0);
|
||||
for (int i = 0; i < static_cast<int>(members.size()); ++i) {
|
||||
index_prefix.back() = i;
|
||||
if (!EmitPipelineOutput(var_name, var_type, decos, index_prefix,
|
||||
members[i], forced_member_type, return_members,
|
||||
return_exprs)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
const bool is_builtin = ast::HasDecoration<ast::BuiltinDecoration>(*decos);
|
||||
|
||||
const Type* member_type = is_builtin ? forced_member_type : tip_type;
|
||||
// Derive the member name directly from the variable name. They can't
|
||||
// collide.
|
||||
const auto member_name = namer_.MakeDerivedName(var_name);
|
||||
// Create the member.
|
||||
// TODO(dneto): Note: If the parameter has non-location decorations,
|
||||
// then those decoration AST nodes will be reused between multiple elements
|
||||
// of a matrix, array, or structure. Normally that's disallowed but currently
|
||||
// the SPIR-V reader will make duplicates when the entire AST is cloned
|
||||
// at the top level of the SPIR-V reader flow. Consider rewriting this
|
||||
// to avoid this node-sharing.
|
||||
return_members->push_back(
|
||||
builder_.Member(member_name, member_type->Build(builder_), *decos));
|
||||
|
||||
// Create an expression to evaluate the part of the variable indexed by
|
||||
// the index_prefix.
|
||||
ast::Expression* load_source = builder_.Expr(var_name);
|
||||
|
||||
// Index into the variable as needed to pick out the flattened member.
|
||||
auto* current_type = var_type->UnwrapAlias()->UnwrapRef()->UnwrapAlias();
|
||||
for (auto index : index_prefix) {
|
||||
if (auto* matrix_type = current_type->As<Matrix>()) {
|
||||
load_source = builder_.IndexAccessor(load_source, builder_.Expr(index));
|
||||
current_type = ty_.Vector(matrix_type->type, matrix_type->rows);
|
||||
} else if (auto* array_type = current_type->As<Array>()) {
|
||||
load_source = builder_.IndexAccessor(load_source, builder_.Expr(index));
|
||||
current_type = array_type->type->UnwrapAlias();
|
||||
} else if (auto* struct_type = current_type->As<Struct>()) {
|
||||
load_source = builder_.MemberAccessor(
|
||||
load_source,
|
||||
builder_.Expr(parser_impl_.GetMemberName(*struct_type, index)));
|
||||
current_type = struct_type->members[index];
|
||||
}
|
||||
}
|
||||
|
||||
if (is_builtin && (tip_type != forced_member_type)) {
|
||||
// The member will have the WGSL type, but we need bitcast to
|
||||
// the variable store type.
|
||||
load_source = create<ast::BitcastExpression>(
|
||||
forced_member_type->Build(builder_), load_source);
|
||||
}
|
||||
return_exprs->push_back(load_source);
|
||||
|
||||
// Increment the location attribute, in case more parameters will follow.
|
||||
for (auto*& deco : *decos) {
|
||||
if (auto* loc_deco = deco->As<ast::LocationDecoration>()) {
|
||||
// Replace this location decoration with a new one with one higher index.
|
||||
// The old one doesn't leak because it's kept in the builder's AST node
|
||||
// list.
|
||||
deco = builder_.Location(loc_deco->source(), loc_deco->value() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
bool FunctionEmitter::EmitEntryPointAsWrapper() {
|
||||
Source source;
|
||||
|
||||
|
@ -1082,7 +1197,6 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
|
|||
// variables must not have them.
|
||||
|
||||
const auto var_name = namer_.GetName(var_id);
|
||||
const auto var_sym = builder_.Symbols().Register(var_name);
|
||||
|
||||
bool ok = true;
|
||||
if (HasBuiltinSampleMask(param_decos)) {
|
||||
|
@ -1091,13 +1205,12 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
|
|||
auto* sample_mask_array_type =
|
||||
store_type->UnwrapRef()->UnwrapAlias()->As<Array>();
|
||||
TINT_ASSERT(Reader, sample_mask_array_type);
|
||||
ok = EmitInputParameter(var_name, store_type, ¶m_decos, {0},
|
||||
sample_mask_array_type->type, forced_param_type,
|
||||
&(decl.params), &stmts);
|
||||
ok = EmitPipelineInput(var_name, store_type, ¶m_decos, {0},
|
||||
sample_mask_array_type->type, forced_param_type,
|
||||
&(decl.params), &stmts);
|
||||
} else {
|
||||
// The normal path.
|
||||
ok =
|
||||
EmitInputParameter(var_name, store_type, ¶m_decos, {}, store_type,
|
||||
ok = EmitPipelineInput(var_name, store_type, ¶m_decos, {}, store_type,
|
||||
forced_param_type, &(decl.params), &stmts);
|
||||
}
|
||||
if (!ok) {
|
||||
|
@ -1127,30 +1240,34 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
|
|||
const auto return_struct_sym =
|
||||
builder_.Symbols().Register(return_struct_name);
|
||||
|
||||
// Define the structure.
|
||||
std::vector<ast::StructMember*> return_members;
|
||||
ast::ExpressionList return_exprs;
|
||||
|
||||
const auto& builtin_position_info = parser_impl_.GetBuiltInPositionInfo();
|
||||
|
||||
// Define the structure.
|
||||
ast::ExpressionList return_exprs;
|
||||
std::vector<ast::StructMember*> return_members;
|
||||
for (uint32_t var_id : ep_info_->outputs) {
|
||||
const Type* param_type = nullptr;
|
||||
const Type* store_type = nullptr;
|
||||
ast::DecorationList out_decos;
|
||||
if (var_id == builtin_position_info.per_vertex_var_id) {
|
||||
// The SPIR-V gl_PerVertex variable has already been remapped to
|
||||
// a gl_Position variable.
|
||||
// Substitute the type.
|
||||
store_type = param_type = ty_.Vector(ty_.F32(), 4);
|
||||
out_decos.emplace_back(
|
||||
create<ast::BuiltinDecoration>(source, ast::Builtin::kPosition));
|
||||
// a gl_Position variable. Substitute the type.
|
||||
const Type* param_type = ty_.Vector(ty_.F32(), 4);
|
||||
ast::DecorationList out_decos{
|
||||
create<ast::BuiltinDecoration>(source, ast::Builtin::kPosition)};
|
||||
|
||||
const auto var_name = namer_.GetName(var_id);
|
||||
return_members.push_back(
|
||||
builder_.Member(var_name, param_type->Build(builder_), out_decos));
|
||||
return_exprs.push_back(builder_.Expr(var_name));
|
||||
|
||||
} else {
|
||||
const auto* var = def_use_mgr_->GetDef(var_id);
|
||||
TINT_ASSERT(Reader, var != nullptr);
|
||||
TINT_ASSERT(Reader, var->opcode() == SpvOpVariable);
|
||||
store_type = GetVariableStoreType(*var);
|
||||
param_type = store_type;
|
||||
if (!parser_impl_.ConvertDecorationsForVariable(var_id, ¶m_type,
|
||||
&out_decos, true)) {
|
||||
const Type* store_type = GetVariableStoreType(*var);
|
||||
const Type* forced_member_type = store_type;
|
||||
ast::DecorationList out_decos;
|
||||
if (!parser_impl_.ConvertDecorationsForVariable(
|
||||
var_id, &forced_member_type, &out_decos, true)) {
|
||||
// This occurs, and is not an error, for the PointSize builtin.
|
||||
if (!success()) {
|
||||
// But exit early if an error was logged.
|
||||
|
@ -1158,45 +1275,29 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
|
|||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(dneto): flatten structs and arrays to vectors or scalars.
|
||||
// The Per-vertex structure is already flattened.
|
||||
|
||||
// The member name is the same as the variable name, which is already
|
||||
// unique across all module-scope declarations.
|
||||
const auto var_name = namer_.GetName(var_id);
|
||||
const auto var_sym = builder_.Symbols().Register(var_name);
|
||||
|
||||
// Form the member type.
|
||||
// Reuse the var name for the member name. They can't clash.
|
||||
ast::StructMember* return_member = create<ast::StructMember>(
|
||||
Source{}, var_sym, param_type->Build(builder_), out_decos);
|
||||
return_members.push_back(return_member);
|
||||
|
||||
ast::Expression* return_member_value =
|
||||
create<ast::IdentifierExpression>(source, var_sym);
|
||||
if (HasBuiltinSampleMask(out_decos)) {
|
||||
// In Vulkan SPIR-V, the sample mask is an array. In WGSL it's a scalar.
|
||||
// Get the first element only.
|
||||
return_member_value = create<ast::ArrayAccessorExpression>(
|
||||
source, return_member_value, parser_impl_.MakeNullValue(ty_.I32()));
|
||||
if (const auto* arr_ty = store_type->UnwrapAlias()->As<Array>()) {
|
||||
if (arr_ty->type->IsSignedScalarOrVector()) {
|
||||
// sample_mask is unsigned in WGSL. Bitcast it.
|
||||
return_member_value = create<ast::BitcastExpression>(
|
||||
source, param_type->Build(builder_), return_member_value);
|
||||
}
|
||||
const auto var_name = namer_.GetName(var_id);
|
||||
bool ok = true;
|
||||
if (HasBuiltinSampleMask(out_decos)) {
|
||||
// In Vulkan SPIR-V, the sample mask is an array. In WGSL it's a
|
||||
// scalar. Use the first element only.
|
||||
auto* sample_mask_array_type =
|
||||
store_type->UnwrapRef()->UnwrapAlias()->As<Array>();
|
||||
TINT_ASSERT(Reader, sample_mask_array_type);
|
||||
ok = EmitPipelineOutput(var_name, store_type, &out_decos, {0},
|
||||
sample_mask_array_type->type,
|
||||
forced_member_type, &return_members,
|
||||
&return_exprs);
|
||||
} else {
|
||||
// Vulkan SPIR-V requires this. Validation should have failed already.
|
||||
return Fail()
|
||||
<< "expected SampleMask to be an array of integer scalars";
|
||||
// The normal path.
|
||||
ok = EmitPipelineOutput(var_name, store_type, &out_decos, {},
|
||||
store_type, forced_member_type,
|
||||
&return_members, &return_exprs);
|
||||
}
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// No other builtin outputs need signedness conversion.
|
||||
}
|
||||
// Save the expression.
|
||||
return_exprs.push_back(return_member_value);
|
||||
}
|
||||
|
||||
if (return_members.empty()) {
|
||||
|
|
|
@ -428,14 +428,43 @@ class FunctionEmitter {
|
|||
/// @param params The parameter list where the new parameter is appended.
|
||||
/// @param statements The statement list where the assignment is appended.
|
||||
/// @returns false if emission failed
|
||||
bool EmitInputParameter(std::string var_name,
|
||||
bool EmitPipelineInput(std::string var_name,
|
||||
const Type* var_type,
|
||||
ast::DecorationList* decos,
|
||||
std::vector<int> index_prefix,
|
||||
const Type* tip_type,
|
||||
const Type* forced_param_type,
|
||||
ast::VariableList* params,
|
||||
ast::StatementList* statements);
|
||||
|
||||
/// Creates one or more struct members from an output variable, and the
|
||||
/// expressions that compute the value they contribute to the entry point
|
||||
/// return value. The part of the output variable is specfied
|
||||
/// by the `index_prefix`, which successively indexes into the variable.
|
||||
/// Assumes the variable has already been created in the Private storage
|
||||
/// class.
|
||||
/// @param var_name The name of the variable
|
||||
/// @param var_type The store type of the variable
|
||||
/// @param decos The variable's decorations
|
||||
/// @param index_prefix Indices stepping into the variable, indicating
|
||||
/// what part of the variable to populate.
|
||||
/// @param tip_type The type of the component inside variable, after indexing
|
||||
/// with the indices in `index_prefix`.
|
||||
/// @param forced_member_type The type forced by WGSL, if the variable is a
|
||||
/// builtin, otherwise the same as var_type.
|
||||
/// @param return_members The struct member list where the new member is
|
||||
/// added.
|
||||
/// @param return_exprs The expression list where the return expression is
|
||||
/// added.
|
||||
/// @returns false if emission failed
|
||||
bool EmitPipelineOutput(std::string var_name,
|
||||
const Type* var_type,
|
||||
ast::DecorationList* decos,
|
||||
std::vector<int> index_prefix,
|
||||
const Type* tip_type,
|
||||
const Type* forced_param_type,
|
||||
ast::VariableList* params,
|
||||
ast::StatementList* statements);
|
||||
const Type* forced_member_type,
|
||||
ast::StructMemberList* return_members,
|
||||
ast::ExpressionList* return_exprs);
|
||||
|
||||
/// Create an ast::BlockStatement representing the body of the function.
|
||||
/// This creates the statement stack, which is non-empty for the lifetime
|
||||
|
|
|
@ -3373,7 +3373,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_Direct) {
|
|||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{sample_mask}
|
||||
]] x_1: __u32}
|
||||
]] x_1_1: __u32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -3438,7 +3438,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_CopyObject) {
|
|||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{sample_mask}
|
||||
]] x_1: __u32}
|
||||
]] x_1_1: __u32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -3503,7 +3503,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_AccessChain) {
|
|||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{sample_mask}
|
||||
]] x_1: __u32}
|
||||
]] x_1_1: __u32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -3567,7 +3567,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_Direct) {
|
|||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{sample_mask}
|
||||
]] x_1: __u32}
|
||||
]] x_1_1: __u32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -3634,7 +3634,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_CopyObject) {
|
|||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{sample_mask}
|
||||
]] x_1: __u32}
|
||||
]] x_1_1: __u32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -3701,7 +3701,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_AccessChain) {
|
|||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{sample_mask}
|
||||
]] x_1: __u32}
|
||||
]] x_1_1: __u32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -3844,7 +3844,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_WithStride) {
|
|||
Arr_3 -> __array__i32_2_stride_4
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{sample_mask}
|
||||
]] x_1: __u32}
|
||||
]] x_1_1: __u32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -5549,9 +5549,9 @@ TEST_F(SpvModuleScopeVarParserTest, EntryPointWrapping_IOLocations) {
|
|||
R"(
|
||||
Struct main_out {
|
||||
StructMember{[[ LocationDecoration{0}
|
||||
]] x_2: __u32}
|
||||
]] x_2_1: __u32}
|
||||
StructMember{[[ LocationDecoration{40}
|
||||
]] x_4: __u32}
|
||||
]] x_4_1: __u32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -5953,7 +5953,7 @@ TEST_F(SpvModuleScopeVarParserTest,
|
|||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{sample_mask}
|
||||
]] x_1: __u32}
|
||||
]] x_1_1: __u32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -6026,7 +6026,7 @@ TEST_F(SpvModuleScopeVarParserTest,
|
|||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{sample_mask}
|
||||
]] x_1: __u32}
|
||||
]] x_1_1: __u32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -6100,7 +6100,7 @@ TEST_F(SpvModuleScopeVarParserTest,
|
|||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{frag_depth}
|
||||
]] x_1: __f32}
|
||||
]] x_1_1: __f32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -6326,7 +6326,7 @@ TEST_F(SpvModuleScopeVarParserTest, Input_FlattenArray_OneLevel) {
|
|||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{position}
|
||||
]] x_2: __vec_4__f32}
|
||||
]] x_2_1: __vec_4__f32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -6453,7 +6453,7 @@ TEST_F(SpvModuleScopeVarParserTest, Input_FlattenMatrix) {
|
|||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{position}
|
||||
]] x_2: __vec_4__f32}
|
||||
]] x_2_1: __vec_4__f32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -6573,7 +6573,7 @@ TEST_F(SpvModuleScopeVarParserTest, Input_FlattenStruct) {
|
|||
}
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{position}
|
||||
]] x_2: __vec_4__f32}
|
||||
]] x_2_1: __vec_4__f32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -6686,7 +6686,7 @@ TEST_F(SpvModuleScopeVarParserTest, Input_FlattenNested) {
|
|||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ BuiltinDecoration{position}
|
||||
]] x_2: __vec_4__f32}
|
||||
]] x_2_1: __vec_4__f32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
|
@ -6805,7 +6805,292 @@ TEST_F(SpvModuleScopeVarParserTest, Input_FlattenNested) {
|
|||
EXPECT_EQ(got, expected) << got;
|
||||
}
|
||||
|
||||
// TODO(dneto): flatting structures
|
||||
TEST_F(SpvModuleScopeVarParserTest, Output_FlattenArray_OneLevel) {
|
||||
const std::string assembly = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint Vertex %main "main" %1 %2
|
||||
OpDecorate %1 Location 4
|
||||
OpDecorate %2 BuiltIn Position
|
||||
|
||||
%void = OpTypeVoid
|
||||
%voidfn = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%v4float = OpTypeVector %float 4
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%uint_1 = OpConstant %uint 1
|
||||
%uint_3 = OpConstant %uint 3
|
||||
%arr = OpTypeArray %float %uint_3
|
||||
%11 = OpTypePointer Output %arr
|
||||
|
||||
%1 = OpVariable %11 Output
|
||||
|
||||
%12 = OpTypePointer Output %v4float
|
||||
%2 = OpVariable %12 Output
|
||||
|
||||
%main = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
auto p = parser(test::Assemble(assembly));
|
||||
|
||||
ASSERT_TRUE(p->Parse()) << p->error() << assembly;
|
||||
EXPECT_TRUE(p->error().empty());
|
||||
|
||||
const auto got = p->program().to_str();
|
||||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ LocationDecoration{4}
|
||||
]] x_1_1: __f32}
|
||||
StructMember{[[ LocationDecoration{5}
|
||||
]] x_1_2: __f32}
|
||||
StructMember{[[ LocationDecoration{6}
|
||||
]] x_1_3: __f32}
|
||||
StructMember{[[ BuiltinDecoration{position}
|
||||
]] x_2_1: __vec_4__f32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
private
|
||||
undefined
|
||||
__array__f32_3
|
||||
}
|
||||
Variable{
|
||||
x_2
|
||||
private
|
||||
undefined
|
||||
__vec_4__f32
|
||||
}
|
||||
Function main_1 -> __void
|
||||
()
|
||||
{
|
||||
Return{}
|
||||
}
|
||||
Function main -> __type_name_main_out
|
||||
StageDecoration{vertex}
|
||||
()
|
||||
{
|
||||
Call[not set]{
|
||||
Identifier[not set]{main_1}
|
||||
(
|
||||
)
|
||||
}
|
||||
Return{
|
||||
{
|
||||
TypeConstructor[not set]{
|
||||
__type_name_main_out
|
||||
ArrayAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
ScalarConstructor[not set]{0}
|
||||
}
|
||||
ArrayAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
ScalarConstructor[not set]{1}
|
||||
}
|
||||
ArrayAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
ScalarConstructor[not set]{2}
|
||||
}
|
||||
Identifier[not set]{x_2}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
EXPECT_EQ(got, expected) << got;
|
||||
}
|
||||
|
||||
TEST_F(SpvModuleScopeVarParserTest, Output_FlattenMatrix) {
|
||||
const std::string assembly = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint Vertex %main "main" %1 %2
|
||||
OpDecorate %1 Location 9
|
||||
OpDecorate %2 BuiltIn Position
|
||||
|
||||
%void = OpTypeVoid
|
||||
%voidfn = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%v4float = OpTypeVector %float 4
|
||||
%m2v4float = OpTypeMatrix %v4float 2
|
||||
%uint = OpTypeInt 32 0
|
||||
|
||||
%11 = OpTypePointer Output %m2v4float
|
||||
|
||||
%1 = OpVariable %11 Output
|
||||
|
||||
%12 = OpTypePointer Output %v4float
|
||||
%2 = OpVariable %12 Output
|
||||
|
||||
%main = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
auto p = parser(test::Assemble(assembly));
|
||||
|
||||
ASSERT_TRUE(p->Parse()) << p->error() << assembly;
|
||||
EXPECT_TRUE(p->error().empty());
|
||||
|
||||
const auto got = p->program().to_str();
|
||||
const std::string expected = R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ LocationDecoration{9}
|
||||
]] x_1_1: __vec_4__f32}
|
||||
StructMember{[[ LocationDecoration{10}
|
||||
]] x_1_2: __vec_4__f32}
|
||||
StructMember{[[ BuiltinDecoration{position}
|
||||
]] x_2_1: __vec_4__f32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
private
|
||||
undefined
|
||||
__mat_4_2__f32
|
||||
}
|
||||
Variable{
|
||||
x_2
|
||||
private
|
||||
undefined
|
||||
__vec_4__f32
|
||||
}
|
||||
Function main_1 -> __void
|
||||
()
|
||||
{
|
||||
Return{}
|
||||
}
|
||||
Function main -> __type_name_main_out
|
||||
StageDecoration{vertex}
|
||||
()
|
||||
{
|
||||
Call[not set]{
|
||||
Identifier[not set]{main_1}
|
||||
(
|
||||
)
|
||||
}
|
||||
Return{
|
||||
{
|
||||
TypeConstructor[not set]{
|
||||
__type_name_main_out
|
||||
ArrayAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
ScalarConstructor[not set]{0}
|
||||
}
|
||||
ArrayAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
ScalarConstructor[not set]{1}
|
||||
}
|
||||
Identifier[not set]{x_2}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
EXPECT_EQ(got, expected) << got;
|
||||
}
|
||||
|
||||
TEST_F(SpvModuleScopeVarParserTest, Output_FlattenStruct) {
|
||||
const std::string assembly = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint Vertex %main "main" %1 %2
|
||||
|
||||
OpName %strct "Communicators"
|
||||
OpMemberName %strct 0 "alice"
|
||||
OpMemberName %strct 1 "bob"
|
||||
|
||||
OpDecorate %1 Location 9
|
||||
OpDecorate %2 BuiltIn Position
|
||||
|
||||
|
||||
%void = OpTypeVoid
|
||||
%voidfn = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%v4float = OpTypeVector %float 4
|
||||
%strct = OpTypeStruct %float %v4float
|
||||
|
||||
%11 = OpTypePointer Output %strct
|
||||
|
||||
%1 = OpVariable %11 Output
|
||||
|
||||
%12 = OpTypePointer Output %v4float
|
||||
%2 = OpVariable %12 Output
|
||||
|
||||
%main = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
auto p = parser(test::Assemble(assembly));
|
||||
|
||||
ASSERT_TRUE(p->Parse()) << p->error() << assembly;
|
||||
EXPECT_TRUE(p->error().empty());
|
||||
|
||||
const auto got = p->program().to_str();
|
||||
const std::string expected = R"(Module{
|
||||
Struct Communicators {
|
||||
StructMember{alice: __f32}
|
||||
StructMember{bob: __vec_4__f32}
|
||||
}
|
||||
Struct main_out {
|
||||
StructMember{[[ LocationDecoration{9}
|
||||
]] x_1_1: __f32}
|
||||
StructMember{[[ LocationDecoration{10}
|
||||
]] x_1_2: __vec_4__f32}
|
||||
StructMember{[[ BuiltinDecoration{position}
|
||||
]] x_2_1: __vec_4__f32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
private
|
||||
undefined
|
||||
__type_name_Communicators
|
||||
}
|
||||
Variable{
|
||||
x_2
|
||||
private
|
||||
undefined
|
||||
__vec_4__f32
|
||||
}
|
||||
Function main_1 -> __void
|
||||
()
|
||||
{
|
||||
Return{}
|
||||
}
|
||||
Function main -> __type_name_main_out
|
||||
StageDecoration{vertex}
|
||||
()
|
||||
{
|
||||
Call[not set]{
|
||||
Identifier[not set]{main_1}
|
||||
(
|
||||
)
|
||||
}
|
||||
Return{
|
||||
{
|
||||
TypeConstructor[not set]{
|
||||
__type_name_main_out
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{alice}
|
||||
}
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{bob}
|
||||
}
|
||||
Identifier[not set]{x_2}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
EXPECT_EQ(got, expected) << got;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace spirv
|
||||
|
|
|
@ -1545,13 +1545,13 @@ void main_1() {
|
|||
}
|
||||
|
||||
struct main_out {
|
||||
float4 x_GLF_color;
|
||||
float4 x_GLF_color_1;
|
||||
};
|
||||
struct tint_symbol_1 {
|
||||
float4 gl_FragCoord_param : SV_Position;
|
||||
};
|
||||
struct tint_symbol_2 {
|
||||
float4 x_GLF_color : SV_Target0;
|
||||
float4 x_GLF_color_1 : SV_Target0;
|
||||
};
|
||||
|
||||
tint_symbol_2 main(tint_symbol_1 tint_symbol) {
|
||||
|
@ -1559,6 +1559,6 @@ tint_symbol_2 main(tint_symbol_1 tint_symbol) {
|
|||
gl_FragCoord = gl_FragCoord_param;
|
||||
main_1();
|
||||
const main_out tint_symbol_3 = {x_GLF_color};
|
||||
const tint_symbol_2 tint_symbol_83 = {tint_symbol_3.x_GLF_color};
|
||||
const tint_symbol_2 tint_symbol_83 = {tint_symbol_3.x_GLF_color_1};
|
||||
return tint_symbol_83;
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ struct buf0 {
|
|||
/* 0x0000 */ packed_float2 resolution;
|
||||
};
|
||||
struct main_out {
|
||||
float4 x_GLF_color;
|
||||
float4 x_GLF_color_1;
|
||||
};
|
||||
struct tint_symbol_2 {
|
||||
float4 x_GLF_color [[color(0)]];
|
||||
float4 x_GLF_color_1 [[color(0)]];
|
||||
};
|
||||
|
||||
void swap_i1_i1_(thread int* const i, thread int* const j, thread QuicksortObject* const tint_symbol_83) {
|
||||
|
@ -1553,8 +1553,8 @@ fragment tint_symbol_2 tint_symbol(float4 gl_FragCoord_param [[position]], const
|
|||
thread float4 tint_symbol_91 = 0.0f;
|
||||
tint_symbol_89 = gl_FragCoord_param;
|
||||
main_1(x_188, &(tint_symbol_90), &(tint_symbol_89), &(tint_symbol_91));
|
||||
main_out const tint_symbol_3 = {.x_GLF_color=tint_symbol_91};
|
||||
tint_symbol_2 const tint_symbol_82 = {.x_GLF_color=tint_symbol_3.x_GLF_color};
|
||||
main_out const tint_symbol_3 = {.x_GLF_color_1=tint_symbol_91};
|
||||
tint_symbol_2 const tint_symbol_82 = {.x_GLF_color_1=tint_symbol_3.x_GLF_color_1};
|
||||
return tint_symbol_82;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
OpName %i_2 "i_2"
|
||||
OpName %uv "uv"
|
||||
OpName %main_out "main_out"
|
||||
OpMemberName %main_out 0 "x_GLF_color"
|
||||
OpMemberName %main_out 0 "x_GLF_color_1"
|
||||
OpName %tint_symbol_3 "tint_symbol_3"
|
||||
OpName %tint_symbol_1 "tint_symbol_1"
|
||||
OpName %main "main"
|
||||
|
|
|
@ -1494,7 +1494,7 @@ fn main_1() {
|
|||
|
||||
struct main_out {
|
||||
[[location(0)]]
|
||||
x_GLF_color : vec4<f32>;
|
||||
x_GLF_color_1 : vec4<f32>;
|
||||
};
|
||||
|
||||
[[stage(fragment)]]
|
||||
|
|
Loading…
Reference in New Issue