// 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 "src/ast/transform/vertex_pulling_transform.h" #include "gtest/gtest.h" #include "src/ast/decorated_variable.h" #include "src/ast/function.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/void_type.h" #include "src/type_determiner.h" #include "src/validator.h" namespace tint { namespace ast { namespace transform { namespace { class VertexPullingTransformHelper { public: VertexPullingTransformHelper() { mod_ = std::make_unique(); transform_ = std::make_unique(&ctx_, mod_.get()); } // Create basic module with an entry point and vertex function void InitBasicModule() { mod()->AddEntryPoint(std::make_unique(PipelineStage::kVertex, "main", "vtx_main")); mod()->AddFunction(std::make_unique( "vtx_main", VariableList{}, ctx_.type_mgr().Get(std::make_unique()))); } // Set up the transformation, after building the module void InitTransform(VertexStateDescriptor vertex_state) { EXPECT_TRUE(mod_->IsValid()); tint::TypeDeterminer td(&ctx_, mod_.get()); EXPECT_TRUE(td.Determine()); tint::Validator v; EXPECT_TRUE(v.Validate(mod_.get())); transform_->SetVertexState( std::make_unique(std::move(vertex_state))); transform_->SetEntryPoint("main"); } // Inserts a variable which will be converted to vertex pulling void AddVertexInputVariable(uint32_t location, std::string name, type::Type* type) { auto var = std::make_unique( std::make_unique(name, StorageClass::kInput, type)); VariableDecorationList decorations; decorations.push_back(std::make_unique(location)); var->set_decorations(std::move(decorations)); mod_->AddGlobalVariable(std::move(var)); } ast::Module* mod() { return mod_.get(); } VertexPullingTransform* transform() { return transform_.get(); } private: Context ctx_; std::unique_ptr mod_; std::unique_ptr transform_; }; class VertexPullingTransformTest : public VertexPullingTransformHelper, public testing::Test {}; TEST_F(VertexPullingTransformTest, Error_NoVertexState) { EXPECT_FALSE(transform()->Run()); EXPECT_EQ(transform()->GetError(), "SetVertexState not called"); } TEST_F(VertexPullingTransformTest, Error_NoEntryPoint) { transform()->SetVertexState(std::make_unique()); EXPECT_FALSE(transform()->Run()); EXPECT_EQ(transform()->GetError(), "Vertex stage entry point not found"); } TEST_F(VertexPullingTransformTest, Error_InvalidEntryPoint) { InitBasicModule(); InitTransform({}); transform()->SetEntryPoint("_"); EXPECT_FALSE(transform()->Run()); EXPECT_EQ(transform()->GetError(), "Vertex stage entry point not found"); } TEST_F(VertexPullingTransformTest, Error_EntryPointWrongStage) { InitBasicModule(); mod()->entry_points()[0]->set_pipeline_stage(PipelineStage::kFragment); InitTransform({}); EXPECT_FALSE(transform()->Run()); EXPECT_EQ(transform()->GetError(), "Entry point is not for vertex stage"); } TEST_F(VertexPullingTransformTest, BasicModule) { InitBasicModule(); InitTransform({}); EXPECT_TRUE(transform()->Run()); } TEST_F(VertexPullingTransformTest, OneAttribute) { InitBasicModule(); type::F32Type f32; AddVertexInputVariable(0, "var_a", &f32); InitTransform({{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}}); EXPECT_TRUE(transform()->Run()); EXPECT_EQ(R"(Module{ Variable{ var_a private __f32 } DecoratedVariable{ Decorations{ BuiltinDecoration{vertex_idx} } tint_pulling_vertex_index in __i32 } DecoratedVariable{ Decorations{ BindingDecoration{0} SetDecoration{0} } tint_pulling_vertex_buffer_0 storage_buffer __struct_ } EntryPoint{vertex as main = vtx_main} Function vtx_main -> __void () { Block{ VariableDeclStatement{ Variable{ tint_pulling_pos function __i32 } } Assignment{ Identifier{tint_pulling_pos} Binary{ Binary{ Identifier{tint_pulling_vertex_index} multiply ScalarConstructor{4} } add ScalarConstructor{0} } } Assignment{ Identifier{var_a} As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_0} Identifier{data} } Binary{ Identifier{tint_pulling_pos} divide ScalarConstructor{4} } } } } } } } )", mod()->to_str()); } // We expect the transform to use an existing vertex_idx builtin variable if it // finds one TEST_F(VertexPullingTransformTest, ExistingVertexIndex) { InitBasicModule(); type::F32Type f32; AddVertexInputVariable(0, "var_a", &f32); type::I32Type i32; auto vertex_index_var = std::make_unique(std::make_unique( "custom_vertex_index", StorageClass::kInput, &i32)); VariableDecorationList decorations; decorations.push_back( std::make_unique(Builtin::kVertexIdx)); vertex_index_var->set_decorations(std::move(decorations)); mod()->AddGlobalVariable(std::move(vertex_index_var)); InitTransform({{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}}); EXPECT_TRUE(transform()->Run()); EXPECT_EQ(R"(Module{ Variable{ var_a private __f32 } DecoratedVariable{ Decorations{ BuiltinDecoration{vertex_idx} } custom_vertex_index in __i32 } DecoratedVariable{ Decorations{ BindingDecoration{0} SetDecoration{0} } tint_pulling_vertex_buffer_0 storage_buffer __struct_ } EntryPoint{vertex as main = vtx_main} Function vtx_main -> __void () { Block{ VariableDeclStatement{ Variable{ tint_pulling_pos function __i32 } } Assignment{ Identifier{tint_pulling_pos} Binary{ Binary{ Identifier{custom_vertex_index} multiply ScalarConstructor{4} } add ScalarConstructor{0} } } Assignment{ Identifier{var_a} As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_0} Identifier{data} } Binary{ Identifier{tint_pulling_pos} divide ScalarConstructor{4} } } } } } } } )", mod()->to_str()); } TEST_F(VertexPullingTransformTest, TwoAttributesSameBuffer) { InitBasicModule(); type::F32Type f32; AddVertexInputVariable(0, "var_a", &f32); type::ArrayType vec4_f32{&f32, 4u}; AddVertexInputVariable(1, "var_b", &vec4_f32); InitTransform( {{{16, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}, {VertexFormat::kVec4F32, 0, 1}}}}}); EXPECT_TRUE(transform()->Run()); EXPECT_EQ(R"(Module{ Variable{ var_a private __f32 } Variable{ var_b private __array__f32_4 } DecoratedVariable{ Decorations{ BuiltinDecoration{vertex_idx} } tint_pulling_vertex_index in __i32 } DecoratedVariable{ Decorations{ BindingDecoration{0} SetDecoration{0} } tint_pulling_vertex_buffer_0 storage_buffer __struct_ } EntryPoint{vertex as main = vtx_main} Function vtx_main -> __void () { Block{ VariableDeclStatement{ Variable{ tint_pulling_pos function __i32 } } Assignment{ Identifier{tint_pulling_pos} Binary{ Binary{ Identifier{tint_pulling_vertex_index} multiply ScalarConstructor{16} } add ScalarConstructor{0} } } Assignment{ Identifier{var_a} As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_0} Identifier{data} } Binary{ Identifier{tint_pulling_pos} divide ScalarConstructor{4} } } } } Assignment{ Identifier{tint_pulling_pos} Binary{ Binary{ Identifier{tint_pulling_vertex_index} multiply ScalarConstructor{16} } add ScalarConstructor{0} } } Assignment{ Identifier{var_b} TypeConstructor{ __vec_4__f32 As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_0} Identifier{data} } Binary{ Binary{ Identifier{tint_pulling_pos} add ScalarConstructor{0} } divide ScalarConstructor{4} } } } As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_0} Identifier{data} } Binary{ Binary{ Identifier{tint_pulling_pos} add ScalarConstructor{4} } divide ScalarConstructor{4} } } } As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_0} Identifier{data} } Binary{ Binary{ Identifier{tint_pulling_pos} add ScalarConstructor{8} } divide ScalarConstructor{4} } } } As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_0} Identifier{data} } Binary{ Binary{ Identifier{tint_pulling_pos} add ScalarConstructor{12} } divide ScalarConstructor{4} } } } } } } } } )", mod()->to_str()); } TEST_F(VertexPullingTransformTest, FloatVectorAttributes) { InitBasicModule(); type::F32Type f32; type::ArrayType vec2_f32{&f32, 2u}; AddVertexInputVariable(0, "var_a", &vec2_f32); type::ArrayType vec3_f32{&f32, 3u}; AddVertexInputVariable(1, "var_b", &vec3_f32); type::ArrayType vec4_f32{&f32, 4u}; AddVertexInputVariable(2, "var_c", &vec4_f32); InitTransform( {{{8, InputStepMode::kVertex, {{VertexFormat::kVec2F32, 0, 0}}}, {12, InputStepMode::kVertex, {{VertexFormat::kVec3F32, 0, 1}}}, {16, InputStepMode::kVertex, {{VertexFormat::kVec4F32, 0, 2}}}}}); EXPECT_TRUE(transform()->Run()); EXPECT_EQ(R"(Module{ Variable{ var_a private __array__f32_2 } Variable{ var_b private __array__f32_3 } Variable{ var_c private __array__f32_4 } DecoratedVariable{ Decorations{ BuiltinDecoration{vertex_idx} } tint_pulling_vertex_index in __i32 } DecoratedVariable{ Decorations{ BindingDecoration{0} SetDecoration{0} } tint_pulling_vertex_buffer_0 storage_buffer __struct_ } DecoratedVariable{ Decorations{ BindingDecoration{1} SetDecoration{0} } tint_pulling_vertex_buffer_1 storage_buffer __struct_ } DecoratedVariable{ Decorations{ BindingDecoration{2} SetDecoration{0} } tint_pulling_vertex_buffer_2 storage_buffer __struct_ } EntryPoint{vertex as main = vtx_main} Function vtx_main -> __void () { Block{ VariableDeclStatement{ Variable{ tint_pulling_pos function __i32 } } Assignment{ Identifier{tint_pulling_pos} Binary{ Binary{ Identifier{tint_pulling_vertex_index} multiply ScalarConstructor{8} } add ScalarConstructor{0} } } Assignment{ Identifier{var_a} TypeConstructor{ __vec_2__f32 As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_0} Identifier{data} } Binary{ Binary{ Identifier{tint_pulling_pos} add ScalarConstructor{0} } divide ScalarConstructor{4} } } } As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_0} Identifier{data} } Binary{ Binary{ Identifier{tint_pulling_pos} add ScalarConstructor{4} } divide ScalarConstructor{4} } } } } } Assignment{ Identifier{tint_pulling_pos} Binary{ Binary{ Identifier{tint_pulling_vertex_index} multiply ScalarConstructor{12} } add ScalarConstructor{0} } } Assignment{ Identifier{var_b} TypeConstructor{ __vec_3__f32 As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_1} Identifier{data} } Binary{ Binary{ Identifier{tint_pulling_pos} add ScalarConstructor{0} } divide ScalarConstructor{4} } } } As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_1} Identifier{data} } Binary{ Binary{ Identifier{tint_pulling_pos} add ScalarConstructor{4} } divide ScalarConstructor{4} } } } As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_1} Identifier{data} } Binary{ Binary{ Identifier{tint_pulling_pos} add ScalarConstructor{8} } divide ScalarConstructor{4} } } } } } Assignment{ Identifier{tint_pulling_pos} Binary{ Binary{ Identifier{tint_pulling_vertex_index} multiply ScalarConstructor{16} } add ScalarConstructor{0} } } Assignment{ Identifier{var_c} TypeConstructor{ __vec_4__f32 As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_2} Identifier{data} } Binary{ Binary{ Identifier{tint_pulling_pos} add ScalarConstructor{0} } divide ScalarConstructor{4} } } } As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_2} Identifier{data} } Binary{ Binary{ Identifier{tint_pulling_pos} add ScalarConstructor{4} } divide ScalarConstructor{4} } } } As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_2} Identifier{data} } Binary{ Binary{ Identifier{tint_pulling_pos} add ScalarConstructor{8} } divide ScalarConstructor{4} } } } As<__f32>{ ArrayAccessor{ MemberAccessor{ Identifier{tint_pulling_vertex_buffer_2} Identifier{data} } Binary{ Binary{ Identifier{tint_pulling_pos} add ScalarConstructor{12} } divide ScalarConstructor{4} } } } } } } } } )", mod()->to_str()); } } // namespace } // namespace transform } // namespace ast } // namespace tint