// 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 #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/context.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::F32Type f32; ast::StructMemberList members; ast::StructMemberDecorationList deco; deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("mem", &f32, std::move(deco))); auto strct = std::make_unique(); strct->set_members(std::move(members)); ast::type::StructType s("Str", std::move(strct)); auto str_var = std::make_unique( std::make_unique("str", ast::StorageClass::kPrivate, &s)); auto str = std::make_unique("str"); auto mem = std::make_unique("mem"); ast::MemberAccessorExpression expr(std::move(str), std::move(mem)); td().RegisterVariableForTesting(str_var.get()); gen().register_global(str_var.get()); mod()->AddGlobalVariable(std::move(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 data : Data; // data.b; // // -> asfloat(data.Load(4)); ast::type::F32Type f32; ast::type::I32Type i32; ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &i32, std::move(a_deco))); ast::StructMemberDecorationList b_deco; b_deco.push_back( std::make_unique(4, Source{})); members.push_back( std::make_unique("b", &f32, std::move(b_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); ast::MemberAccessorExpression expr( std::make_unique("data"), std::make_unique("b")); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(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 data : Data; // data.a; // // -> asint(data.Load(0)); ast::type::F32Type f32; ast::type::I32Type i32; ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &i32, std::move(a_deco))); ast::StructMemberDecorationList b_deco; b_deco.push_back( std::make_unique(4, Source{})); members.push_back( std::make_unique("b", &f32, std::move(b_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); ast::MemberAccessorExpression expr( std::make_unique("data"), std::make_unique("a")); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(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; // }; // var data : Data; // mat2x3 b; // data.a = b; // // -> matrix _tint_tmp = b; // data.Store3(4 + 0, asuint(_tint_tmp[0])); // data.Store3(4 + 16, asuint(_tint_tmp[1])); ast::type::F32Type f32; ast::type::I32Type i32; ast::type::MatrixType mat(&f32, 3, 2); ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("z", &i32, std::move(a_deco))); ast::StructMemberDecorationList b_deco; b_deco.push_back( std::make_unique(4, Source{})); members.push_back( std::make_unique("a", &mat, std::move(b_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto b_var = std::make_unique("b", ast::StorageClass::kPrivate, &mat); auto coord_var = std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s); auto lhs = std::make_unique( std::make_unique("data"), std::make_unique("a")); auto rhs = std::make_unique("b"); ast::AssignmentStatement assign(std::move(lhs), std::move(rhs)); td().RegisterVariableForTesting(coord_var.get()); td().RegisterVariableForTesting(b_var.get()); gen().register_global(coord_var.get()); gen().register_global(b_var.get()); mod()->AddGlobalVariable(std::move(coord_var)); mod()->AddGlobalVariable(std::move(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"(matrix _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; // }; // var data : Data; // data.a = mat2x3(); // // -> matrix _tint_tmp = matrix(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::F32Type f32; ast::type::I32Type i32; ast::type::MatrixType mat(&f32, 3, 2); ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("z", &i32, std::move(a_deco))); ast::StructMemberDecorationList b_deco; b_deco.push_back( std::make_unique(4, Source{})); members.push_back( std::make_unique("a", &mat, std::move(b_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); auto lhs = std::make_unique( std::make_unique("data"), std::make_unique("a")); auto rhs = std::make_unique( &mat, ast::ExpressionList{}); ast::AssignmentStatement assign(std::move(lhs), std::move(rhs)); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(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"(matrix _tint_tmp = matrix(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; // }; // var data : Data; // data.a; // // -> asfloat(matrix(data.Load2(4 + 0), data.Load2(4 + 8), // data.Load2(4 + 16))); ast::type::F32Type f32; ast::type::I32Type i32; ast::type::MatrixType mat(&f32, 2, 3); ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("z", &i32, std::move(a_deco))); ast::StructMemberDecorationList b_deco; b_deco.push_back( std::make_unique(4, Source{})); members.push_back( std::make_unique("a", &mat, std::move(b_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); ast::MemberAccessorExpression expr( std::make_unique("data"), std::make_unique("a")); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(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(matrix(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 data : Outer; // data.b.a; // // -> asfloat(matrix(data.Load3(4 + 0), data.Load3(4 + 16))); ast::type::F32Type f32; ast::type::I32Type i32; ast::type::MatrixType mat(&f32, 3, 2); ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("z", &i32, std::move(a_deco))); ast::StructMemberDecorationList b_deco; b_deco.push_back( std::make_unique(4, Source{})); members.push_back( std::make_unique("a", &mat, std::move(b_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); ast::MemberAccessorExpression expr( std::make_unique("data"), std::make_unique("a")); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(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(matrix(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 data : Data; // data.a; // // -> asfloat(matrix(data.Load3(0), data.Load3(16), // data.Load3(32))); ast::type::F32Type f32; ast::type::I32Type i32; ast::type::MatrixType mat(&f32, 3, 3); ast::StructMemberList members; ast::StructMemberDecorationList deco; deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &mat, std::move(deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); ast::MemberAccessorExpression expr( std::make_unique("data"), std::make_unique("a")); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(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(matrix(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; // }; // var data : Data; // data.a[2][1]; // // -> asfloat(data.Load((2 * 16) + (1 * 4) + 16))) ast::type::F32Type f32; ast::type::I32Type i32; ast::type::MatrixType mat(&f32, 3, 4); ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("z", &i32, std::move(a_deco))); ast::StructMemberDecorationList b_deco; b_deco.push_back( std::make_unique(16, Source{})); members.push_back( std::make_unique("a", &mat, std::move(b_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); ast::ArrayAccessorExpression expr( std::make_unique( std::make_unique( std::make_unique("data"), std::make_unique("a")), std::make_unique( std::make_unique(&i32, 2))), std::make_unique( std::make_unique(&i32, 1))); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(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; // }; // var data : Data; // data.a[2]; // // -> asint(data.Load((2 * 4)); ast::type::F32Type f32; ast::type::I32Type i32; ast::type::ArrayType ary(&i32, 5); ast::ArrayDecorationList decos; decos.push_back(std::make_unique(4, Source{})); ary.set_decorations(std::move(decos)); ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &ary, std::move(a_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); ast::ArrayAccessorExpression expr( std::make_unique( std::make_unique("data"), std::make_unique("a")), std::make_unique( std::make_unique(&i32, 2))); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(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; // }; // var data : Data; // data.a[(2 + 4) - 3]; // // -> asint(data.Load((4 * ((2 + 4) - 3))); ast::type::F32Type f32; ast::type::I32Type i32; ast::type::ArrayType ary(&i32, 5); ast::ArrayDecorationList decos; decos.push_back(std::make_unique(4, Source{})); ary.set_decorations(std::move(decos)); ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &ary, std::move(a_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); ast::ArrayAccessorExpression expr( std::make_unique( std::make_unique("data"), std::make_unique("a")), std::make_unique( ast::BinaryOp::kSubtract, std::make_unique( ast::BinaryOp::kAdd, std::make_unique( std::make_unique(&i32, 2)), std::make_unique( std::make_unique(&i32, 4))), std::make_unique( std::make_unique(&i32, 3)))); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(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 data : Data; // data.b = 2.3f; // // -> data.Store(0, asuint(2.0f)); ast::type::F32Type f32; ast::type::I32Type i32; ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &i32, std::move(a_deco))); ast::StructMemberDecorationList b_deco; b_deco.push_back( std::make_unique(4, Source{})); members.push_back( std::make_unique("b", &f32, std::move(b_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(coord_var)); ASSERT_TRUE(td().Determine()) << td().error(); auto lhs = std::make_unique( std::make_unique("data"), std::make_unique("b")); auto rhs = std::make_unique( std::make_unique(&f32, 2.0f)); ast::AssignmentStatement assign(std::move(lhs), std::move(rhs)); ASSERT_TRUE(td().DetermineResultType(&assign)); ASSERT_TRUE(gen().EmitStatement(out(), &assign)) << gen().error(); EXPECT_EQ(result(), R"(data.Store(4, asuint(2.00000000f)); )"); } TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Store_ToArray) { // struct Data { // [[offset(0)]] a : [[stride(4)]] array; // }; // var data : Data; // data.a[2] = 2; // // -> data.Store((2 * 4), asuint(2.3f)); ast::type::F32Type f32; ast::type::I32Type i32; ast::type::ArrayType ary(&i32, 5); ast::ArrayDecorationList decos; decos.push_back(std::make_unique(4, Source{})); ary.set_decorations(std::move(decos)); ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &ary, std::move(a_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(coord_var)); ASSERT_TRUE(td().Determine()) << td().error(); auto lhs = std::make_unique( std::make_unique( std::make_unique("data"), std::make_unique("a")), std::make_unique( std::make_unique(&i32, 2))); auto rhs = std::make_unique( std::make_unique(&i32, 2)); ast::AssignmentStatement assign(std::move(lhs), std::move(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 data : Data; // data.a = 2; // // -> data.Store(0, asuint(2)); ast::type::F32Type f32; ast::type::I32Type i32; ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &i32, std::move(a_deco))); ast::StructMemberDecorationList b_deco; b_deco.push_back( std::make_unique(4, Source{})); members.push_back( std::make_unique("b", &f32, std::move(b_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(coord_var)); ASSERT_TRUE(td().Determine()) << td().error(); auto lhs = std::make_unique( std::make_unique("data"), std::make_unique("a")); auto rhs = std::make_unique( std::make_unique(&i32, 2)); ast::AssignmentStatement assign(std::move(lhs), std::move(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; // [[offset(16)]] b : vec3; // }; // var data : Data; // data.b; // // -> asfloat(data.Load(16)); ast::type::F32Type f32; ast::type::I32Type i32; ast::type::VectorType ivec3(&i32, 3); ast::type::VectorType fvec3(&f32, 3); ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &ivec3, std::move(a_deco))); ast::StructMemberDecorationList b_deco; b_deco.push_back( std::make_unique(16, Source{})); members.push_back( std::make_unique("b", &fvec3, std::move(b_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(coord_var)); ASSERT_TRUE(td().Determine()) << td().error(); ast::MemberAccessorExpression expr( std::make_unique("data"), std::make_unique("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; // [[offset(16)]] b : vec3; // }; // var data : Data; // data.b = vec3(2.3f, 1.2f, 0.2f); // // -> data.Store(16, asuint(vector(2.3f, 1.2f, 0.2f))); ast::type::F32Type f32; ast::type::I32Type i32; ast::type::VectorType ivec3(&i32, 3); ast::type::VectorType fvec3(&f32, 3); ast::StructMemberList members; ast::StructMemberDecorationList a_deco; a_deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &ivec3, std::move(a_deco))); ast::StructMemberDecorationList b_deco; b_deco.push_back( std::make_unique(16, Source{})); members.push_back( std::make_unique("b", &fvec3, std::move(b_deco))); auto str = std::make_unique(); str->set_members(std::move(members)); ast::type::StructType s("Data", std::move(str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &s)); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(coord_var)); ASSERT_TRUE(td().Determine()) << td().error(); auto lit1 = std::make_unique(&f32, 1.f); auto lit2 = std::make_unique(&f32, 2.f); auto lit3 = std::make_unique(&f32, 3.f); ast::ExpressionList values; values.push_back( std::make_unique(std::move(lit1))); values.push_back( std::make_unique(std::move(lit2))); values.push_back( std::make_unique(std::move(lit3))); auto lhs = std::make_unique( std::make_unique("data"), std::make_unique("b")); auto rhs = std::make_unique( &fvec3, std::move(values)); ast::AssignmentStatement assign(std::move(lhs), std::move(rhs)); ASSERT_TRUE(td().DetermineResultType(&assign)); ASSERT_TRUE(gen().EmitStatement(out(), &assign)) << gen().error(); EXPECT_EQ( result(), R"(data.Store3(16, asuint(vector(1.00000000f, 2.00000000f, 3.00000000f))); )"); } TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel) { // struct Data { // [[offset(0)]] a : vec3; // [[offset(16)]] b : vec3; // }; // struct Pre { // var c : [[stride(32)]] array; // }; // // var data : Pre; // data.c[2].b // // -> asfloat(data.Load3(16 + (2 * 32))) ast::type::F32Type f32; ast::type::I32Type i32; ast::type::VectorType ivec3(&i32, 3); ast::type::VectorType fvec3(&f32, 3); ast::StructMemberList members; ast::StructMemberDecorationList deco; deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &ivec3, std::move(deco))); deco.push_back( std::make_unique(16, Source{})); members.push_back( std::make_unique("b", &fvec3, std::move(deco))); auto data_str = std::make_unique(); data_str->set_members(std::move(members)); ast::type::StructType data("Data", std::move(data_str)); ast::type::ArrayType ary(&data, 4); ast::ArrayDecorationList decos; decos.push_back(std::make_unique(32, Source{})); ary.set_decorations(std::move(decos)); deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("c", &ary, std::move(deco))); auto pre_str = std::make_unique(); pre_str->set_members(std::move(members)); ast::type::StructType pre_struct("Pre", std::move(pre_str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &pre_struct)); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(coord_var)); ASSERT_TRUE(td().Determine()) << td().error(); ast::MemberAccessorExpression expr( std::make_unique( std::make_unique( std::make_unique("data"), std::make_unique("c")), std::make_unique( std::make_unique(&i32, 2))), std::make_unique("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; // [[offset(16)]] b : vec3; // }; // struct Pre { // var c : [[stride(32)]] array; // }; // // var data : Pre; // data.c[2].b.xy // // -> asfloat(data.Load3(16 + (2 * 32))).xy ast::type::F32Type f32; ast::type::I32Type i32; ast::type::VectorType ivec3(&i32, 3); ast::type::VectorType fvec3(&f32, 3); ast::StructMemberList members; ast::StructMemberDecorationList deco; deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &ivec3, std::move(deco))); deco.push_back( std::make_unique(16, Source{})); members.push_back( std::make_unique("b", &fvec3, std::move(deco))); auto data_str = std::make_unique(); data_str->set_members(std::move(members)); ast::type::StructType data("Data", std::move(data_str)); ast::type::ArrayType ary(&data, 4); ast::ArrayDecorationList decos; decos.push_back(std::make_unique(32, Source{})); ary.set_decorations(std::move(decos)); deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("c", &ary, std::move(deco))); auto pre_str = std::make_unique(); pre_str->set_members(std::move(members)); ast::type::StructType pre_struct("Pre", std::move(pre_str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &pre_struct)); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(coord_var)); ASSERT_TRUE(td().Determine()) << td().error(); ast::MemberAccessorExpression expr( std::make_unique( std::make_unique( std::make_unique( std::make_unique("data"), std::make_unique("c")), std::make_unique( std::make_unique(&i32, 2))), std::make_unique("b")), std::make_unique("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; // [[offset(16)]] b : vec3; // }; // struct Pre { // var c : [[stride(32)]] array; // }; // // var data : Pre; // data.c[2].b.g // // -> asfloat(data.Load((4 * 1) + 16 + (2 * 32) + 0)) ast::type::F32Type f32; ast::type::I32Type i32; ast::type::VectorType ivec3(&i32, 3); ast::type::VectorType fvec3(&f32, 3); ast::StructMemberList members; ast::StructMemberDecorationList deco; deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &ivec3, std::move(deco))); deco.push_back( std::make_unique(16, Source{})); members.push_back( std::make_unique("b", &fvec3, std::move(deco))); auto data_str = std::make_unique(); data_str->set_members(std::move(members)); ast::type::StructType data("Data", std::move(data_str)); ast::type::ArrayType ary(&data, 4); ast::ArrayDecorationList decos; decos.push_back(std::make_unique(32, Source{})); ary.set_decorations(std::move(decos)); deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("c", &ary, std::move(deco))); auto pre_str = std::make_unique(); pre_str->set_members(std::move(members)); ast::type::StructType pre_struct("Pre", std::move(pre_str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &pre_struct)); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(coord_var)); ASSERT_TRUE(td().Determine()) << td().error(); ast::MemberAccessorExpression expr( std::make_unique( std::make_unique( std::make_unique( std::make_unique("data"), std::make_unique("c")), std::make_unique( std::make_unique(&i32, 2))), std::make_unique("b")), std::make_unique("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; // [[offset(16)]] b : vec3; // }; // struct Pre { // var c : [[stride(32)]] array; // }; // // var data : Pre; // data.c[2].b[1] // // -> asfloat(data.Load(4 + 16 + (2 * 32))) ast::type::F32Type f32; ast::type::I32Type i32; ast::type::VectorType ivec3(&i32, 3); ast::type::VectorType fvec3(&f32, 3); ast::StructMemberList members; ast::StructMemberDecorationList deco; deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &ivec3, std::move(deco))); deco.push_back( std::make_unique(16, Source{})); members.push_back( std::make_unique("b", &fvec3, std::move(deco))); auto data_str = std::make_unique(); data_str->set_members(std::move(members)); ast::type::StructType data("Data", std::move(data_str)); ast::type::ArrayType ary(&data, 4); ast::ArrayDecorationList decos; decos.push_back(std::make_unique(32, Source{})); ary.set_decorations(std::move(decos)); deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("c", &ary, std::move(deco))); auto pre_str = std::make_unique(); pre_str->set_members(std::move(members)); ast::type::StructType pre_struct("Pre", std::move(pre_str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &pre_struct)); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(coord_var)); ASSERT_TRUE(td().Determine()) << td().error(); ast::ArrayAccessorExpression expr( std::make_unique( std::make_unique( std::make_unique( std::make_unique("data"), std::make_unique("c")), std::make_unique( std::make_unique(&i32, 2))), std::make_unique("b")), std::make_unique( std::make_unique(&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; // [[offset(16)]] b : vec3; // }; // struct Pre { // var c : [[stride(32)]] array; // }; // // var data : Pre; // data.c[2].b = vec3(1.f, 2.f, 3.f); // // -> data.Store3(16 + (2 * 32), asuint(vector(1.0f, 2.0f, 3.0f))); ast::type::F32Type f32; ast::type::I32Type i32; ast::type::VectorType ivec3(&i32, 3); ast::type::VectorType fvec3(&f32, 3); ast::StructMemberList members; ast::StructMemberDecorationList deco; deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &ivec3, std::move(deco))); deco.push_back( std::make_unique(16, Source{})); members.push_back( std::make_unique("b", &fvec3, std::move(deco))); auto data_str = std::make_unique(); data_str->set_members(std::move(members)); ast::type::StructType data("Data", std::move(data_str)); ast::type::ArrayType ary(&data, 4); ast::ArrayDecorationList decos; decos.push_back(std::make_unique(32, Source{})); ary.set_decorations(std::move(decos)); deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("c", &ary, std::move(deco))); auto pre_str = std::make_unique(); pre_str->set_members(std::move(members)); ast::type::StructType pre_struct("Pre", std::move(pre_str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &pre_struct)); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(coord_var)); ASSERT_TRUE(td().Determine()) << td().error(); auto lhs = std::make_unique( std::make_unique( std::make_unique( std::make_unique("data"), std::make_unique("c")), std::make_unique( std::make_unique(&i32, 2))), std::make_unique("b")); auto lit1 = std::make_unique(&f32, 1.f); auto lit2 = std::make_unique(&f32, 2.f); auto lit3 = std::make_unique(&f32, 3.f); ast::ExpressionList values; values.push_back( std::make_unique(std::move(lit1))); values.push_back( std::make_unique(std::move(lit2))); values.push_back( std::make_unique(std::move(lit3))); auto rhs = std::make_unique( &fvec3, std::move(values)); ast::AssignmentStatement assign(std::move(lhs), std::move(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(vector(1.00000000f, 2.00000000f, 3.00000000f))); )"); } TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Store_Swizzle_SingleLetter) { // struct Data { // [[offset(0)]] a : vec3; // [[offset(16)]] b : vec3; // }; // struct Pre { // var c : [[stride(32)]] array; // }; // // var data : Pre; // data.c[2].b.y = 1.f; // // -> data.Store((4 * 1) + 16 + (2 * 32) + 0, asuint(1.0f)); ast::type::F32Type f32; ast::type::I32Type i32; ast::type::VectorType ivec3(&i32, 3); ast::type::VectorType fvec3(&f32, 3); ast::StructMemberList members; ast::StructMemberDecorationList deco; deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("a", &ivec3, std::move(deco))); deco.push_back( std::make_unique(16, Source{})); members.push_back( std::make_unique("b", &fvec3, std::move(deco))); auto data_str = std::make_unique(); data_str->set_members(std::move(members)); ast::type::StructType data("Data", std::move(data_str)); ast::type::ArrayType ary(&data, 4); ast::ArrayDecorationList decos; decos.push_back(std::make_unique(32, Source{})); ary.set_decorations(std::move(decos)); deco.push_back( std::make_unique(0, Source{})); members.push_back( std::make_unique("c", &ary, std::move(deco))); auto pre_str = std::make_unique(); pre_str->set_members(std::move(members)); ast::type::StructType pre_struct("Pre", std::move(pre_str)); auto coord_var = std::make_unique(std::make_unique( "data", ast::StorageClass::kStorageBuffer, &pre_struct)); td().RegisterVariableForTesting(coord_var.get()); gen().register_global(coord_var.get()); mod()->AddGlobalVariable(std::move(coord_var)); ASSERT_TRUE(td().Determine()) << td().error(); auto lhs = std::make_unique( std::make_unique( std::make_unique( std::make_unique( std::make_unique("data"), std::make_unique("c")), std::make_unique( std::make_unique(&i32, 2))), std::make_unique("b")), std::make_unique("y")); auto rhs = std::make_unique( std::make_unique(&i32, 1.f)); ast::AssignmentStatement assign(std::move(lhs), std::move(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.00000000f)); )"); } } // namespace } // namespace hlsl } // namespace writer } // namespace tint