blob: 5e1cc955e34213c56ebc2a42afed27c3f9b84fe4 [file] [log] [blame]
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include "src/ast/array_accessor_expression.h"
#include "src/ast/assignment_statement.h"
#include "src/ast/binary_expression.h"
#include "src/ast/decorated_variable.h"
#include "src/ast/float_literal.h"
#include "src/ast/identifier_expression.h"
#include "src/ast/member_accessor_expression.h"
#include "src/ast/module.h"
#include "src/ast/scalar_constructor_expression.h"
#include "src/ast/sint_literal.h"
#include "src/ast/stride_decoration.h"
#include "src/ast/struct.h"
#include "src/ast/struct_member.h"
#include "src/ast/struct_member_offset_decoration.h"
#include "src/ast/type/array_type.h"
#include "src/ast/type/f32_type.h"
#include "src/ast/type/i32_type.h"
#include "src/ast/type/matrix_type.h"
#include "src/ast/type/struct_type.h"
#include "src/ast/type/vector_type.h"
#include "src/ast/type_constructor_expression.h"
#include "src/type_determiner.h"
#include "src/writer/hlsl/test_helper.h"
namespace tint {
namespace writer {
namespace hlsl {
namespace {
using HlslGeneratorImplTest_MemberAccessor = TestHelper;
TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor) {
ast::type::F32 f32;
ast::StructMemberList members;
ast::StructMemberDecorationList deco;
deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("mem", &f32, deco));
auto* strct = create<ast::Struct>(members);
ast::type::Struct s("Str", strct);
auto* str_var = create<ast::DecoratedVariable>(
create<ast::Variable>(Source{}, "str", ast::StorageClass::kPrivate, &s));
auto* str = create<ast::IdentifierExpression>("str");
auto* mem = create<ast::IdentifierExpression>("mem");
ast::MemberAccessorExpression expr(str, mem);
td.RegisterVariableForTesting(str_var);
gen.register_global(str_var);
mod.AddGlobalVariable(str_var);
ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(), "str.mem");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load) {
// struct Data {
// [[offset(0)]] a : i32;
// [[offset(4)]] b : f32;
// };
// var<storage_buffer> data : Data;
// data.b;
//
// -> asfloat(data.Load(4));
ast::type::F32 f32;
ast::type::I32 i32;
ast::StructMemberList members;
ast::StructMemberDecorationList a_deco;
a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("a", &i32, a_deco));
ast::StructMemberDecorationList b_deco;
b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4, Source{}));
members.push_back(create<ast::StructMember>("b", &f32, b_deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
ast::MemberAccessorExpression expr(create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("b"));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(td.DetermineResultType(&expr));
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(), "asfloat(data.Load(4))");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_Int) {
// struct Data {
// [[offset(0)]] a : i32;
// [[offset(4)]] b : f32;
// };
// var<storage_buffer> data : Data;
// data.a;
//
// -> asint(data.Load(0));
ast::type::F32 f32;
ast::type::I32 i32;
ast::StructMemberList members;
ast::StructMemberDecorationList a_deco;
a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("a", &i32, a_deco));
ast::StructMemberDecorationList b_deco;
b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4, Source{}));
members.push_back(create<ast::StructMember>("b", &f32, b_deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
ast::MemberAccessorExpression expr(create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("a"));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(td.DetermineResultType(&expr));
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(), "asint(data.Load(0))");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Store_Matrix) {
// struct Data {
// [[offset(0)]] z : f32;
// [[offset(4)]] a : mat2x3<f32>;
// };
// var<storage_buffer> data : Data;
// mat2x3<f32> b;
// data.a = b;
//
// -> float3x2 _tint_tmp = b;
// data.Store3(4 + 0, asuint(_tint_tmp[0]));
// data.Store3(4 + 16, asuint(_tint_tmp[1]));
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Matrix mat(&f32, 3, 2);
auto* str = create<ast::Struct>(ast::StructMemberList{
create<ast::StructMember>(
"z", &i32,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(0, Source{})}),
create<ast::StructMember>(
"a", &mat,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(4, Source{})}),
});
ast::type::Struct s("Data", str);
auto* b_var =
create<ast::Variable>(Source{}, "b", ast::StorageClass::kPrivate, &mat);
auto* coord_var = create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s);
auto* lhs = create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("a"));
auto* rhs = create<ast::IdentifierExpression>("b");
ast::AssignmentStatement assign(lhs, rhs);
td.RegisterVariableForTesting(coord_var);
td.RegisterVariableForTesting(b_var);
gen.register_global(coord_var);
gen.register_global(b_var);
mod.AddGlobalVariable(coord_var);
mod.AddGlobalVariable(b_var);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(td.DetermineResultType(&assign));
ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
EXPECT_EQ(result(), R"(float3x2 _tint_tmp = b;
data.Store3(4 + 0, asuint(_tint_tmp[0]));
data.Store3(4 + 16, asuint(_tint_tmp[1]));
)");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Store_Matrix_Empty) {
// struct Data {
// [[offset(0)]] z : f32;
// [[offset(4)]] a : mat2x3<f32>;
// };
// var<storage_buffer> data : Data;
// data.a = mat2x3<f32>();
//
// -> float3x2 _tint_tmp = float3x2(0.0f, 0.0f, 0.0f,
// 0.0f, 0.0f, 0.0f);
// data.Store3(4 + 0, asuint(_tint_tmp[0]);
// data.Store3(4 + 16, asuint(_tint_tmp[1]));
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Matrix mat(&f32, 3, 2);
ast::StructMemberList members;
ast::StructMemberDecorationList a_deco;
a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("z", &i32, a_deco));
ast::StructMemberDecorationList b_deco;
b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4, Source{}));
members.push_back(create<ast::StructMember>("a", &mat, b_deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
auto* lhs = create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("a"));
auto* rhs =
create<ast::TypeConstructorExpression>(&mat, ast::ExpressionList{});
ast::AssignmentStatement assign(lhs, rhs);
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(td.DetermineResultType(&assign));
ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
EXPECT_EQ(
result(),
R"(float3x2 _tint_tmp = float3x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
data.Store3(4 + 0, asuint(_tint_tmp[0]));
data.Store3(4 + 16, asuint(_tint_tmp[1]));
)");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix) {
// struct Data {
// [[offset(0)]] z : f32;
// [[offset(4)]] a : mat3x2<f32>;
// };
// var<storage_buffer> data : Data;
// data.a;
//
// -> asfloat(uint2x3(data.Load2(4 + 0), data.Load2(4 + 8),
// data.Load2(4 + 16)));
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Matrix mat(&f32, 2, 3);
ast::StructMemberList members;
ast::StructMemberDecorationList a_deco;
a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("z", &i32, a_deco));
ast::StructMemberDecorationList b_deco;
b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4, Source{}));
members.push_back(create<ast::StructMember>("a", &mat, b_deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
ast::MemberAccessorExpression expr(create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("a"));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(td.DetermineResultType(&expr));
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(),
"asfloat(uint2x3(data.Load2(4 + 0), data.Load2(4 + 8), "
"data.Load2(4 + 16)))");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix_Nested) {
// struct Data {
// [[offset(0)]] z : f32;
// [[offset(4)]] a : mat2x3<f32;
// };
// struct Outer {
// [[offset(0)]] c : f32;
// [[offset(4)]] b : Data;
// };
// var<storage_buffer> data : Outer;
// data.b.a;
//
// -> asfloat(uint3x2(data.Load3(4 + 0), data.Load3(4 + 16)));
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Matrix mat(&f32, 3, 2);
ast::StructMemberList members;
ast::StructMemberDecorationList a_deco;
a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("z", &i32, a_deco));
ast::StructMemberDecorationList b_deco;
b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4, Source{}));
members.push_back(create<ast::StructMember>("a", &mat, b_deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
ast::MemberAccessorExpression expr(create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("a"));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(td.DetermineResultType(&expr));
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(),
"asfloat(uint3x2(data.Load3(4 + 0), data.Load3(4 + 16)))");
}
TEST_F(
HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix_By3_Is_16_Bytes) {
// struct Data {
// [[offset(4)]] a : mat3x3<f32;
// };
// var<storage_buffer> data : Data;
// data.a;
//
// -> asfloat(uint3x3(data.Load3(0), data.Load3(16),
// data.Load3(32)));
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Matrix mat(&f32, 3, 3);
ast::StructMemberList members;
ast::StructMemberDecorationList deco;
deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("a", &mat, deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
ast::MemberAccessorExpression expr(create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("a"));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(td.DetermineResultType(&expr));
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(),
"asfloat(uint3x3(data.Load3(0 + 0), data.Load3(0 + 16), "
"data.Load3(0 + 32)))");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix_Single_Element) {
// struct Data {
// [[offset(0)]] z : f32;
// [[offset(16)]] a : mat4x3<f32>;
// };
// var<storage_buffer> data : Data;
// data.a[2][1];
//
// -> asfloat(data.Load((2 * 16) + (1 * 4) + 16)))
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Matrix mat(&f32, 3, 4);
ast::StructMemberList members;
ast::StructMemberDecorationList a_deco;
a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("z", &i32, a_deco));
ast::StructMemberDecorationList b_deco;
b_deco.push_back(create<ast::StructMemberOffsetDecoration>(16, Source{}));
members.push_back(create<ast::StructMember>("a", &mat, b_deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
ast::ArrayAccessorExpression expr(
create<ast::ArrayAccessorExpression>(
create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("a")),
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 2))),
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 1)));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(td.DetermineResultType(&expr));
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(), "asfloat(data.Load((4 * 1) + (16 * 2) + 16))");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_ArrayAccessor_StorageBuffer_Load_Int_FromArray) {
// struct Data {
// [[offset(0)]] a : [[stride(4)]] array<i32, 5>;
// };
// var<storage_buffer> data : Data;
// data.a[2];
//
// -> asint(data.Load((2 * 4));
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Array ary(&i32, 5,
ast::ArrayDecorationList{
create<ast::StrideDecoration>(4, Source{}),
});
ast::StructMemberList members;
ast::StructMemberDecorationList a_deco;
a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("a", &ary, a_deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
ast::ArrayAccessorExpression expr(
create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("a")),
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 2)));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(), "asint(data.Load((4 * 2) + 0))");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_ArrayAccessor_StorageBuffer_Load_Int_FromArray_ExprIdx) {
// struct Data {
// [[offset(0)]] a : [[stride(4)]] array<i32, 5>;
// };
// var<storage_buffer> data : Data;
// data.a[(2 + 4) - 3];
//
// -> asint(data.Load((4 * ((2 + 4) - 3)));
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Array ary(&i32, 5,
ast::ArrayDecorationList{
create<ast::StrideDecoration>(4, Source{}),
});
ast::StructMemberList members;
ast::StructMemberDecorationList a_deco;
a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("a", &ary, a_deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
ast::ArrayAccessorExpression expr(
create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("a")),
create<ast::BinaryExpression>(
ast::BinaryOp::kSubtract,
create<ast::BinaryExpression>(
ast::BinaryOp::kAdd,
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 2)),
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 4))),
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 3))));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(), "asint(data.Load((4 * ((2 + 4) - 3)) + 0))");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Store) {
// struct Data {
// [[offset(0)]] a : i32;
// [[offset(4)]] b : f32;
// };
// var<storage_buffer> data : Data;
// data.b = 2.3f;
//
// -> data.Store(0, asuint(2.0f));
ast::type::F32 f32;
ast::type::I32 i32;
ast::StructMemberList members;
ast::StructMemberDecorationList a_deco;
a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("a", &i32, a_deco));
ast::StructMemberDecorationList b_deco;
b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4, Source{}));
members.push_back(create<ast::StructMember>("b", &f32, b_deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
auto* lhs = create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("b"));
auto* rhs = create<ast::ScalarConstructorExpression>(
create<ast::FloatLiteral>(&f32, 2.0f));
ast::AssignmentStatement assign(lhs, rhs);
ASSERT_TRUE(td.DetermineResultType(&assign));
ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
EXPECT_EQ(result(), R"(data.Store(4, asuint(2.0f));
)");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Store_ToArray) {
// struct Data {
// [[offset(0)]] a : [[stride(4)]] array<i32, 5>;
// };
// var<storage_buffer> data : Data;
// data.a[2] = 2;
//
// -> data.Store((2 * 4), asuint(2.3f));
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Array ary(&i32, 5,
ast::ArrayDecorationList{
create<ast::StrideDecoration>(4, Source{}),
});
ast::StructMemberList members;
ast::StructMemberDecorationList a_deco;
a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("a", &ary, a_deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
auto* lhs = create<ast::ArrayAccessorExpression>(
create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("a")),
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 2)));
auto* rhs = create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 2));
ast::AssignmentStatement assign(lhs, rhs);
ASSERT_TRUE(td.DetermineResultType(&assign)) << td.error();
ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
EXPECT_EQ(result(), R"(data.Store((4 * 2) + 0, asuint(2));
)");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Store_Int) {
// struct Data {
// [[offset(0)]] a : i32;
// [[offset(4)]] b : f32;
// };
// var<storage_buffer> data : Data;
// data.a = 2;
//
// -> data.Store(0, asuint(2));
ast::type::F32 f32;
ast::type::I32 i32;
ast::StructMemberList members;
ast::StructMemberDecorationList a_deco;
a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("a", &i32, a_deco));
ast::StructMemberDecorationList b_deco;
b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4, Source{}));
members.push_back(create<ast::StructMember>("b", &f32, b_deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
auto* lhs = create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("a"));
auto* rhs = create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 2));
ast::AssignmentStatement assign(lhs, rhs);
ASSERT_TRUE(td.DetermineResultType(&assign));
ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
EXPECT_EQ(result(), R"(data.Store(0, asuint(2));
)");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_Vec3) {
// struct Data {
// [[offset(0)]] a : vec3<i32>;
// [[offset(16)]] b : vec3<f32>;
// };
// var<storage_buffer> data : Data;
// data.b;
//
// -> asfloat(data.Load(16));
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Vector ivec3(&i32, 3);
ast::type::Vector fvec3(&f32, 3);
ast::StructMemberList members;
ast::StructMemberDecorationList a_deco;
a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("a", &ivec3, a_deco));
ast::StructMemberDecorationList b_deco;
b_deco.push_back(create<ast::StructMemberOffsetDecoration>(16, Source{}));
members.push_back(create<ast::StructMember>("b", &fvec3, b_deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ast::MemberAccessorExpression expr(create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("b"));
ASSERT_TRUE(td.DetermineResultType(&expr));
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(), "asfloat(data.Load3(16))");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Store_Vec3) {
// struct Data {
// [[offset(0)]] a : vec3<i32>;
// [[offset(16)]] b : vec3<f32>;
// };
// var<storage_buffer> data : Data;
// data.b = vec3<f32>(2.3f, 1.2f, 0.2f);
//
// -> data.Store(16, asuint(float3(2.3f, 1.2f, 0.2f)));
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Vector ivec3(&i32, 3);
ast::type::Vector fvec3(&f32, 3);
ast::StructMemberList members;
ast::StructMemberDecorationList a_deco;
a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0, Source{}));
members.push_back(create<ast::StructMember>("a", &ivec3, a_deco));
ast::StructMemberDecorationList b_deco;
b_deco.push_back(create<ast::StructMemberOffsetDecoration>(16, Source{}));
members.push_back(create<ast::StructMember>("b", &fvec3, b_deco));
auto* str = create<ast::Struct>(members);
ast::type::Struct s("Data", str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &s));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
auto* lit1 = create<ast::FloatLiteral>(&f32, 1.f);
auto* lit2 = create<ast::FloatLiteral>(&f32, 2.f);
auto* lit3 = create<ast::FloatLiteral>(&f32, 3.f);
ast::ExpressionList values;
values.push_back(create<ast::ScalarConstructorExpression>(lit1));
values.push_back(create<ast::ScalarConstructorExpression>(lit2));
values.push_back(create<ast::ScalarConstructorExpression>(lit3));
auto* lhs = create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("b"));
auto* rhs = create<ast::TypeConstructorExpression>(&fvec3, values);
ast::AssignmentStatement assign(lhs, rhs);
ASSERT_TRUE(td.DetermineResultType(&assign));
ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
EXPECT_EQ(result(),
R"(data.Store3(16, asuint(float3(1.0f, 2.0f, 3.0f)));
)");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel) {
// struct Data {
// [[offset(0)]] a : vec3<i32>;
// [[offset(16)]] b : vec3<f32>;
// };
// struct Pre {
// var c : [[stride(32)]] array<Data, 4>;
// };
//
// var<storage_buffer> data : Pre;
// data.c[2].b
//
// -> asfloat(data.Load3(16 + (2 * 32)))
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Vector ivec3(&i32, 3);
ast::type::Vector fvec3(&f32, 3);
auto* data_str = create<ast::Struct>(ast::StructMemberList{
create<ast::StructMember>(
"a", &ivec3,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(0, Source{})}),
create<ast::StructMember>(
"b", &fvec3,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(16, Source{})}),
});
ast::type::Struct data("Data", data_str);
ast::type::Array ary(&data, 4,
ast::ArrayDecorationList{
create<ast::StrideDecoration>(32, Source{}),
});
auto* pre_str = create<ast::Struct>(ast::StructMemberList{
create<ast::StructMember>(
"c", &ary,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(0, Source{})}),
});
ast::type::Struct pre_struct("Pre", pre_str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &pre_struct));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ast::MemberAccessorExpression expr(
create<ast::ArrayAccessorExpression>(
create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("c")),
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 2))),
create<ast::IdentifierExpression>("b"));
ASSERT_TRUE(td.DetermineResultType(&expr));
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(), "asfloat(data.Load3(16 + (32 * 2) + 0))");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel_Swizzle) {
// struct Data {
// [[offset(0)]] a : vec3<i32>;
// [[offset(16)]] b : vec3<f32>;
// };
// struct Pre {
// var c : [[stride(32)]] array<Data, 4>;
// };
//
// var<storage_buffer> data : Pre;
// data.c[2].b.xy
//
// -> asfloat(data.Load3(16 + (2 * 32))).xy
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Vector ivec3(&i32, 3);
ast::type::Vector fvec3(&f32, 3);
ast::StructMemberList members;
ast::StructMemberDecorationList deco;
auto* data_str = create<ast::Struct>(ast::StructMemberList{
create<ast::StructMember>(
"a", &ivec3,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(0, Source{})}),
create<ast::StructMember>(
"b", &fvec3,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(16, Source{})}),
});
ast::type::Struct data("Data", data_str);
ast::type::Array ary(
&data, 4,
ast::ArrayDecorationList{create<ast::StrideDecoration>(32, Source{})});
auto* pre_str =
create<ast::Struct>(ast::StructMemberList{create<ast::StructMember>(
"c", &ary,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(0, Source{})})});
ast::type::Struct pre_struct("Pre", pre_str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &pre_struct));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ast::MemberAccessorExpression expr(
create<ast::MemberAccessorExpression>(
create<ast::ArrayAccessorExpression>(
create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("c")),
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 2))),
create<ast::IdentifierExpression>("b")),
create<ast::IdentifierExpression>("xy"));
ASSERT_TRUE(td.DetermineResultType(&expr));
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(), "asfloat(data.Load3(16 + (32 * 2) + 0)).xy");
}
TEST_F(
HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel_Swizzle_SingleLetter) { // NOLINT
// struct Data {
// [[offset(0)]] a : vec3<i32>;
// [[offset(16)]] b : vec3<f32>;
// };
// struct Pre {
// var c : [[stride(32)]] array<Data, 4>;
// };
//
// var<storage_buffer> data : Pre;
// data.c[2].b.g
//
// -> asfloat(data.Load((4 * 1) + 16 + (2 * 32) + 0))
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Vector ivec3(&i32, 3);
ast::type::Vector fvec3(&f32, 3);
auto* data_str = create<ast::Struct>(ast::StructMemberList{
create<ast::StructMember>(
"a", &ivec3,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(0, Source{})}),
create<ast::StructMember>(
"b", &fvec3,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(16, Source{})}),
});
ast::type::Struct data("Data", data_str);
ast::type::Array ary(&data, 4,
ast::ArrayDecorationList{
create<ast::StrideDecoration>(32, Source{}),
});
auto* pre_str =
create<ast::Struct>(ast::StructMemberList{create<ast::StructMember>(
"c", &ary,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(0, Source{})})});
ast::type::Struct pre_struct("Pre", pre_str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &pre_struct));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ast::MemberAccessorExpression expr(
create<ast::MemberAccessorExpression>(
create<ast::ArrayAccessorExpression>(
create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("c")),
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 2))),
create<ast::IdentifierExpression>("b")),
create<ast::IdentifierExpression>("g"));
ASSERT_TRUE(td.DetermineResultType(&expr));
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(), "asfloat(data.Load((4 * 1) + 16 + (32 * 2) + 0))");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel_Index) {
// struct Data {
// [[offset(0)]] a : vec3<i32>;
// [[offset(16)]] b : vec3<f32>;
// };
// struct Pre {
// var c : [[stride(32)]] array<Data, 4>;
// };
//
// var<storage_buffer> data : Pre;
// data.c[2].b[1]
//
// -> asfloat(data.Load(4 + 16 + (2 * 32)))
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Vector ivec3(&i32, 3);
ast::type::Vector fvec3(&f32, 3);
auto* data_str = create<ast::Struct>(ast::StructMemberList{
create<ast::StructMember>(
"a", &ivec3,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(0, Source{})}),
create<ast::StructMember>(
"b", &fvec3,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(16, Source{})}),
});
ast::type::Struct data("Data", data_str);
ast::type::Array ary(&data, 4,
ast::ArrayDecorationList{
create<ast::StrideDecoration>(32, Source{}),
});
auto* pre_str =
create<ast::Struct>(ast::StructMemberList{create<ast::StructMember>(
"c", &ary,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(0, Source{})})});
ast::type::Struct pre_struct("Pre", pre_str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &pre_struct));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
ast::ArrayAccessorExpression expr(
create<ast::MemberAccessorExpression>(
create<ast::ArrayAccessorExpression>(
create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("c")),
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 2))),
create<ast::IdentifierExpression>("b")),
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 1)));
ASSERT_TRUE(td.DetermineResultType(&expr));
ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
EXPECT_EQ(result(), "asfloat(data.Load((4 * 1) + 16 + (32 * 2) + 0))");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Store_MultiLevel) {
// struct Data {
// [[offset(0)]] a : vec3<i32>;
// [[offset(16)]] b : vec3<f32>;
// };
// struct Pre {
// var c : [[stride(32)]] array<Data, 4>;
// };
//
// var<storage_buffer> data : Pre;
// data.c[2].b = vec3<f32>(1.f, 2.f, 3.f);
//
// -> data.Store3(16 + (2 * 32), asuint(float3(1.0f, 2.0f, 3.0f)));
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Vector ivec3(&i32, 3);
ast::type::Vector fvec3(&f32, 3);
auto* data_str = create<ast::Struct>(ast::StructMemberList{
create<ast::StructMember>(
"a", &ivec3,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(0, Source{})}),
create<ast::StructMember>(
"b", &fvec3,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(16, Source{})}),
});
ast::type::Struct data("Data", data_str);
ast::type::Array ary(&data, 4,
ast::ArrayDecorationList{
create<ast::StrideDecoration>(32, Source{}),
});
auto* pre_str =
create<ast::Struct>(ast::StructMemberList{create<ast::StructMember>(
"c", &ary,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(0, Source{})})});
ast::type::Struct pre_struct("Pre", pre_str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &pre_struct));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
auto* lhs = create<ast::MemberAccessorExpression>(
create<ast::ArrayAccessorExpression>(
create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("c")),
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 2))),
create<ast::IdentifierExpression>("b"));
auto* lit1 = create<ast::FloatLiteral>(&f32, 1.f);
auto* lit2 = create<ast::FloatLiteral>(&f32, 2.f);
auto* lit3 = create<ast::FloatLiteral>(&f32, 3.f);
ast::ExpressionList values;
values.push_back(create<ast::ScalarConstructorExpression>(lit1));
values.push_back(create<ast::ScalarConstructorExpression>(lit2));
values.push_back(create<ast::ScalarConstructorExpression>(lit3));
auto* rhs = create<ast::TypeConstructorExpression>(&fvec3, values);
ast::AssignmentStatement assign(lhs, rhs);
ASSERT_TRUE(td.DetermineResultType(&assign));
ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
EXPECT_EQ(result(),
R"(data.Store3(16 + (32 * 2) + 0, asuint(float3(1.0f, 2.0f, 3.0f)));
)");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Store_Swizzle_SingleLetter) {
// struct Data {
// [[offset(0)]] a : vec3<i32>;
// [[offset(16)]] b : vec3<f32>;
// };
// struct Pre {
// var c : [[stride(32)]] array<Data, 4>;
// };
//
// var<storage_buffer> data : Pre;
// data.c[2].b.y = 1.f;
//
// -> data.Store((4 * 1) + 16 + (2 * 32) + 0, asuint(1.0f));
ast::type::F32 f32;
ast::type::I32 i32;
ast::type::Vector ivec3(&i32, 3);
ast::type::Vector fvec3(&f32, 3);
auto* data_str = create<ast::Struct>(ast::StructMemberList{
create<ast::StructMember>(
"a", &ivec3,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(0, Source{})}),
create<ast::StructMember>(
"b", &fvec3,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(16, Source{})}),
});
ast::type::Struct data("Data", data_str);
ast::type::Array ary(&data, 4,
ast::ArrayDecorationList{
create<ast::StrideDecoration>(32, Source{}),
});
auto* pre_str =
create<ast::Struct>(ast::StructMemberList{create<ast::StructMember>(
"c", &ary,
ast::StructMemberDecorationList{
create<ast::StructMemberOffsetDecoration>(0, Source{})})});
ast::type::Struct pre_struct("Pre", pre_str);
auto* coord_var = create<ast::DecoratedVariable>(create<ast::Variable>(
Source{}, "data", ast::StorageClass::kStorageBuffer, &pre_struct));
td.RegisterVariableForTesting(coord_var);
gen.register_global(coord_var);
mod.AddGlobalVariable(coord_var);
ASSERT_TRUE(td.Determine()) << td.error();
auto* lhs = create<ast::MemberAccessorExpression>(
create<ast::MemberAccessorExpression>(
create<ast::ArrayAccessorExpression>(
create<ast::MemberAccessorExpression>(
create<ast::IdentifierExpression>("data"),
create<ast::IdentifierExpression>("c")),
create<ast::ScalarConstructorExpression>(
create<ast::SintLiteral>(&i32, 2))),
create<ast::IdentifierExpression>("b")),
create<ast::IdentifierExpression>("y"));
auto* rhs = create<ast::ScalarConstructorExpression>(
create<ast::FloatLiteral>(&i32, 1.f));
ast::AssignmentStatement assign(lhs, rhs);
ASSERT_TRUE(td.DetermineResultType(&assign));
ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
EXPECT_EQ(result(),
R"(data.Store((4 * 1) + 16 + (32 * 2) + 0, asuint(1.0f));
)");
}
} // namespace
} // namespace hlsl
} // namespace writer
} // namespace tint