1 // Copyright 2020 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/ast/stage_decoration.h"
16 #include "src/ast/workgroup_decoration.h"
17 #include "src/writer/spirv/spv_dump.h"
18 #include "src/writer/spirv/test_helper.h"
19
20 namespace tint {
21 namespace writer {
22 namespace spirv {
23 namespace {
24
25 using BuilderTest = TestHelper;
26
TEST_F(BuilderTest,Decoration_Stage)27 TEST_F(BuilderTest, Decoration_Stage) {
28 auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
29 ast::DecorationList{
30 Stage(ast::PipelineStage::kFragment),
31 });
32
33 spirv::Builder& b = Build();
34
35 ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
36 EXPECT_EQ(DumpInstructions(b.entry_points()),
37 R"(OpEntryPoint Fragment %3 "main"
38 )");
39 }
40
41 struct FunctionStageData {
42 ast::PipelineStage stage;
43 SpvExecutionModel model;
44 };
operator <<(std::ostream & out,FunctionStageData data)45 inline std::ostream& operator<<(std::ostream& out, FunctionStageData data) {
46 out << data.stage;
47 return out;
48 }
49 using Decoration_StageTest = TestParamHelper<FunctionStageData>;
TEST_P(Decoration_StageTest,Emit)50 TEST_P(Decoration_StageTest, Emit) {
51 auto params = GetParam();
52
53 const ast::Variable* var = nullptr;
54 const ast::Type* ret_type = nullptr;
55 ast::DecorationList ret_type_decos;
56 ast::StatementList body;
57 if (params.stage == ast::PipelineStage::kVertex) {
58 ret_type = ty.vec4<f32>();
59 ret_type_decos.push_back(Builtin(ast::Builtin::kPosition));
60 body.push_back(Return(Construct(ty.vec4<f32>())));
61 } else {
62 ret_type = ty.void_();
63 }
64
65 auto deco_list = ast::DecorationList{Stage(params.stage)};
66 if (params.stage == ast::PipelineStage::kCompute) {
67 deco_list.push_back(WorkgroupSize(1));
68 }
69
70 auto* func = Func("main", {}, ret_type, body, deco_list, ret_type_decos);
71
72 spirv::Builder& b = Build();
73
74 if (var) {
75 ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
76 }
77 ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
78
79 auto preamble = b.entry_points();
80 ASSERT_GE(preamble.size(), 1u);
81 EXPECT_EQ(preamble[0].opcode(), spv::Op::OpEntryPoint);
82
83 ASSERT_GE(preamble[0].operands().size(), 3u);
84 EXPECT_EQ(preamble[0].operands()[0].to_i(),
85 static_cast<uint32_t>(params.model));
86 }
87 INSTANTIATE_TEST_SUITE_P(
88 BuilderTest,
89 Decoration_StageTest,
90 testing::Values(FunctionStageData{ast::PipelineStage::kVertex,
91 SpvExecutionModelVertex},
92 FunctionStageData{ast::PipelineStage::kFragment,
93 SpvExecutionModelFragment},
94 FunctionStageData{ast::PipelineStage::kCompute,
95 SpvExecutionModelGLCompute}));
96
TEST_F(BuilderTest,Decoration_ExecutionMode_Fragment_OriginUpperLeft)97 TEST_F(BuilderTest, Decoration_ExecutionMode_Fragment_OriginUpperLeft) {
98 auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
99 ast::DecorationList{
100 Stage(ast::PipelineStage::kFragment),
101 });
102
103 spirv::Builder& b = Build();
104
105 ASSERT_TRUE(b.GenerateExecutionModes(func, 3)) << b.error();
106 EXPECT_EQ(DumpInstructions(b.execution_modes()),
107 R"(OpExecutionMode %3 OriginUpperLeft
108 )");
109 }
110
TEST_F(BuilderTest,Decoration_ExecutionMode_WorkgroupSize_Default)111 TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_Default) {
112 auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
113 ast::DecorationList{Stage(ast::PipelineStage::kCompute),
114 WorkgroupSize(1)});
115
116 spirv::Builder& b = Build();
117
118 ASSERT_TRUE(b.GenerateExecutionModes(func, 3)) << b.error();
119 EXPECT_EQ(DumpInstructions(b.execution_modes()),
120 R"(OpExecutionMode %3 LocalSize 1 1 1
121 )");
122 }
123
TEST_F(BuilderTest,Decoration_ExecutionMode_WorkgroupSize_Literals)124 TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_Literals) {
125 auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
126 ast::DecorationList{
127 WorkgroupSize(2, 4, 6),
128 Stage(ast::PipelineStage::kCompute),
129 });
130
131 spirv::Builder& b = Build();
132
133 ASSERT_TRUE(b.GenerateExecutionModes(func, 3)) << b.error();
134 EXPECT_EQ(DumpInstructions(b.execution_modes()),
135 R"(OpExecutionMode %3 LocalSize 2 4 6
136 )");
137 }
138
TEST_F(BuilderTest,Decoration_ExecutionMode_WorkgroupSize_Const)139 TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_Const) {
140 GlobalConst("width", ty.i32(), Construct(ty.i32(), 2));
141 GlobalConst("height", ty.i32(), Construct(ty.i32(), 3));
142 GlobalConst("depth", ty.i32(), Construct(ty.i32(), 4));
143 auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
144 ast::DecorationList{
145 WorkgroupSize("width", "height", "depth"),
146 Stage(ast::PipelineStage::kCompute),
147 });
148
149 spirv::Builder& b = Build();
150
151 ASSERT_TRUE(b.GenerateExecutionModes(func, 3)) << b.error();
152 EXPECT_EQ(DumpInstructions(b.execution_modes()),
153 R"(OpExecutionMode %3 LocalSize 2 3 4
154 )");
155 }
156
TEST_F(BuilderTest,Decoration_ExecutionMode_WorkgroupSize_OverridableConst)157 TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_OverridableConst) {
158 GlobalConst("width", ty.i32(), Construct(ty.i32(), 2), {Override(7u)});
159 GlobalConst("height", ty.i32(), Construct(ty.i32(), 3), {Override(8u)});
160 GlobalConst("depth", ty.i32(), Construct(ty.i32(), 4), {Override(9u)});
161 auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
162 ast::DecorationList{
163 WorkgroupSize("width", "height", "depth"),
164 Stage(ast::PipelineStage::kCompute),
165 });
166
167 spirv::Builder& b = Build();
168
169 ASSERT_TRUE(b.GenerateExecutionModes(func, 3)) << b.error();
170 EXPECT_EQ(DumpInstructions(b.execution_modes()), "");
171 EXPECT_EQ(DumpInstructions(b.types()),
172 R"(%2 = OpTypeInt 32 0
173 %1 = OpTypeVector %2 3
174 %4 = OpSpecConstant %2 2
175 %5 = OpSpecConstant %2 3
176 %6 = OpSpecConstant %2 4
177 %3 = OpSpecConstantComposite %1 %4 %5 %6
178 )");
179 EXPECT_EQ(DumpInstructions(b.annots()),
180 R"(OpDecorate %4 SpecId 7
181 OpDecorate %5 SpecId 8
182 OpDecorate %6 SpecId 9
183 OpDecorate %3 BuiltIn WorkgroupSize
184 )");
185 }
186
TEST_F(BuilderTest,Decoration_ExecutionMode_WorkgroupSize_LiteralAndConst)187 TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_LiteralAndConst) {
188 GlobalConst("height", ty.i32(), Construct(ty.i32(), 2), {Override(7u)});
189 GlobalConst("depth", ty.i32(), Construct(ty.i32(), 3));
190 auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
191 ast::DecorationList{
192 WorkgroupSize(4, "height", "depth"),
193 Stage(ast::PipelineStage::kCompute),
194 });
195
196 spirv::Builder& b = Build();
197
198 ASSERT_TRUE(b.GenerateExecutionModes(func, 3)) << b.error();
199 EXPECT_EQ(DumpInstructions(b.execution_modes()), "");
200 EXPECT_EQ(DumpInstructions(b.types()),
201 R"(%2 = OpTypeInt 32 0
202 %1 = OpTypeVector %2 3
203 %4 = OpConstant %2 4
204 %5 = OpSpecConstant %2 2
205 %6 = OpConstant %2 3
206 %3 = OpSpecConstantComposite %1 %4 %5 %6
207 )");
208 EXPECT_EQ(DumpInstructions(b.annots()),
209 R"(OpDecorate %5 SpecId 7
210 OpDecorate %3 BuiltIn WorkgroupSize
211 )");
212 }
213
TEST_F(BuilderTest,Decoration_ExecutionMode_MultipleFragment)214 TEST_F(BuilderTest, Decoration_ExecutionMode_MultipleFragment) {
215 auto* func1 = Func("main1", {}, ty.void_(), ast::StatementList{},
216 ast::DecorationList{
217 Stage(ast::PipelineStage::kFragment),
218 });
219
220 auto* func2 = Func("main2", {}, ty.void_(), ast::StatementList{},
221 ast::DecorationList{
222 Stage(ast::PipelineStage::kFragment),
223 });
224
225 spirv::Builder& b = Build();
226
227 ASSERT_TRUE(b.GenerateFunction(func1)) << b.error();
228 ASSERT_TRUE(b.GenerateFunction(func2)) << b.error();
229 EXPECT_EQ(DumpBuilder(b),
230 R"(OpEntryPoint Fragment %3 "main1"
231 OpEntryPoint Fragment %5 "main2"
232 OpExecutionMode %3 OriginUpperLeft
233 OpExecutionMode %5 OriginUpperLeft
234 OpName %3 "main1"
235 OpName %5 "main2"
236 %2 = OpTypeVoid
237 %1 = OpTypeFunction %2
238 %3 = OpFunction %2 None %1
239 %4 = OpLabel
240 OpReturn
241 OpFunctionEnd
242 %5 = OpFunction %2 None %1
243 %6 = OpLabel
244 OpReturn
245 OpFunctionEnd
246 )");
247 }
248
TEST_F(BuilderTest,Decoration_ExecutionMode_FragDepth)249 TEST_F(BuilderTest, Decoration_ExecutionMode_FragDepth) {
250 Func("main", ast::VariableList{}, ty.f32(),
251 ast::StatementList{
252 Return(Expr(1.f)),
253 },
254 ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
255 ast::DecorationList{
256 Builtin(ast::Builtin::kFragDepth),
257 });
258
259 spirv::Builder& b = SanitizeAndBuild();
260
261 ASSERT_TRUE(b.Build());
262
263 EXPECT_EQ(DumpInstructions(b.execution_modes()),
264 R"(OpExecutionMode %11 OriginUpperLeft
265 OpExecutionMode %11 DepthReplacing
266 )");
267 }
268
269 } // namespace
270 } // namespace spirv
271 } // namespace writer
272 } // namespace tint
273