// 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 "gtest/gtest.h" #include "src/ast/builtin.h" #include "src/ast/builtin_decoration.h" #include "src/ast/location_decoration.h" #include "src/ast/return_statement.h" #include "src/ast/stage_decoration.h" #include "src/ast/storage_class.h" #include "src/ast/variable.h" #include "src/program.h" #include "src/type/f32_type.h" #include "src/type/vector_type.h" #include "src/writer/spirv/builder.h" #include "src/writer/spirv/spv_dump.h" #include "src/writer/spirv/test_helper.h" namespace tint { namespace writer { namespace spirv { namespace { using BuilderTest = TestHelper; TEST_F(BuilderTest, EntryPoint_Parameters) { // [[stage(fragment)]] // fn frag_main([[builtin(frag_coord)]] coord : vec4, // [[location(1)]] loc1 : f32) -> void { // var col : f32 = (coord.x * loc1); // } auto* f32 = ty.f32(); auto* vec4 = ty.vec4(); auto* coord = Var("coord", vec4, ast::StorageClass::kInput, nullptr, {create(ast::Builtin::kFragCoord)}); auto* loc1 = Var("loc1", f32, ast::StorageClass::kInput, nullptr, {create(1u)}); auto* mul = Mul(Expr(MemberAccessor("coord", "x")), Expr("loc1")); auto* col = Var("col", f32, ast::StorageClass::kFunction, mul, {}); Func("frag_main", ast::VariableList{coord, loc1}, ty.void_(), ast::StatementList{WrapInStatement(col)}, ast::DecorationList{ create(ast::PipelineStage::kFragment), }); spirv::Builder& b = SanitizeAndBuild(); ASSERT_TRUE(b.Build()); // Test that "coord" and "loc1" get hoisted out to global variables with the // Input storage class, retaining their decorations. EXPECT_EQ(DumpBuilder(b), R"(OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %9 "frag_main" %1 %5 OpExecutionMode %9 OriginUpperLeft OpName %1 "tint_symbol_1" OpName %5 "tint_symbol_2" OpName %9 "frag_main" OpName %17 "col" OpDecorate %1 BuiltIn FragCoord OpDecorate %5 Location 1 %4 = OpTypeFloat 32 %3 = OpTypeVector %4 4 %2 = OpTypePointer Input %3 %1 = OpVariable %2 Input %6 = OpTypePointer Input %4 %5 = OpVariable %6 Input %8 = OpTypeVoid %7 = OpTypeFunction %8 %11 = OpTypeInt 32 0 %12 = OpConstant %11 0 %18 = OpTypePointer Function %4 %19 = OpConstantNull %4 %9 = OpFunction %8 None %7 %10 = OpLabel %17 = OpVariable %18 Function %19 %13 = OpAccessChain %6 %1 %12 %14 = OpLoad %4 %13 %15 = OpLoad %4 %5 %16 = OpFMul %4 %14 %15 OpStore %17 %16 OpReturn OpFunctionEnd )"); } TEST_F(BuilderTest, EntryPoint_ReturnValue) { // [[stage(fragment)]] // fn frag_main([[location(0)]] loc_in : u32) -> [[location(0)]] f32 { // if (loc_in > 10) { // return 0.5; // } // return 1.0; // } auto* f32 = ty.f32(); auto* u32 = ty.u32(); auto* loc_in = Var("loc_in", u32, ast::StorageClass::kFunction, nullptr, {create(0)}); auto* cond = create(ast::BinaryOp::kGreaterThan, Expr("loc_in"), Expr(10u)); Func("frag_main", ast::VariableList{loc_in}, f32, ast::StatementList{ If(cond, Block(create(Expr(0.5f)))), create(Expr(1.0f)), }, ast::DecorationList{ create(ast::PipelineStage::kFragment), }, ast::DecorationList{create(0)}); spirv::Builder& b = SanitizeAndBuild(); ASSERT_TRUE(b.Build()); // Test that the return value gets hoisted out to a global variable with the // Output storage class, and the return statements are replaced with stores. EXPECT_EQ(DumpBuilder(b), R"(OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %10 "frag_main" %1 %4 OpExecutionMode %10 OriginUpperLeft OpName %1 "tint_symbol_1" OpName %4 "tint_symbol_2" OpName %10 "frag_main" OpDecorate %1 Location 0 OpDecorate %4 Location 0 %3 = OpTypeInt 32 0 %2 = OpTypePointer Input %3 %1 = OpVariable %2 Input %6 = OpTypeFloat 32 %5 = OpTypePointer Output %6 %7 = OpConstantNull %6 %4 = OpVariable %5 Output %7 %9 = OpTypeVoid %8 = OpTypeFunction %9 %13 = OpConstant %3 10 %15 = OpTypeBool %18 = OpConstant %6 0.5 %19 = OpConstant %6 1 %10 = OpFunction %9 None %8 %11 = OpLabel %12 = OpLoad %3 %1 %14 = OpUGreaterThan %15 %12 %13 OpSelectionMerge %16 None OpBranchConditional %14 %17 %16 %17 = OpLabel OpStore %4 %18 OpReturn %16 = OpLabel OpStore %4 %19 OpReturn OpFunctionEnd )"); } } // namespace } // namespace spirv } // namespace writer } // namespace tint