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/reader/spirv/enum_converter.h"
16
17 #include <string>
18
19 #include "gmock/gmock.h"
20
21 namespace tint {
22 namespace reader {
23 namespace spirv {
24 namespace {
25
26 // Pipeline stage
27
28 struct PipelineStageCase {
29 SpvExecutionModel model;
30 bool expect_success;
31 ast::PipelineStage expected;
32 };
operator <<(std::ostream & out,PipelineStageCase psc)33 inline std::ostream& operator<<(std::ostream& out, PipelineStageCase psc) {
34 out << "PipelineStageCase{ SpvExecutionModel:" << int(psc.model)
35 << " expect_success?:" << int(psc.expect_success)
36 << " expected:" << int(psc.expected) << "}";
37 return out;
38 }
39
40 class SpvPipelineStageTest : public testing::TestWithParam<PipelineStageCase> {
41 public:
SpvPipelineStageTest()42 SpvPipelineStageTest()
43 : success_(true),
44 fail_stream_(&success_, &errors_),
45 converter_(fail_stream_) {}
46
error() const47 std::string error() const { return errors_.str(); }
48
49 protected:
50 bool success_ = true;
51 std::stringstream errors_;
52 FailStream fail_stream_;
53 EnumConverter converter_;
54 };
55
TEST_P(SpvPipelineStageTest,Samples)56 TEST_P(SpvPipelineStageTest, Samples) {
57 const auto params = GetParam();
58
59 const auto result = converter_.ToPipelineStage(params.model);
60 EXPECT_EQ(success_, params.expect_success);
61 if (params.expect_success) {
62 EXPECT_EQ(result, params.expected);
63 EXPECT_TRUE(error().empty());
64 } else {
65 EXPECT_EQ(result, params.expected);
66 EXPECT_THAT(error(),
67 ::testing::StartsWith("unknown SPIR-V execution model:"));
68 }
69 }
70
71 INSTANTIATE_TEST_SUITE_P(
72 EnumConverterGood,
73 SpvPipelineStageTest,
74 testing::Values(PipelineStageCase{SpvExecutionModelVertex, true,
75 ast::PipelineStage::kVertex},
76 PipelineStageCase{SpvExecutionModelFragment, true,
77 ast::PipelineStage::kFragment},
78 PipelineStageCase{SpvExecutionModelGLCompute, true,
79 ast::PipelineStage::kCompute}));
80
81 INSTANTIATE_TEST_SUITE_P(
82 EnumConverterBad,
83 SpvPipelineStageTest,
84 testing::Values(PipelineStageCase{static_cast<SpvExecutionModel>(9999),
85 false, ast::PipelineStage::kNone},
86 PipelineStageCase{SpvExecutionModelTessellationControl,
87 false, ast::PipelineStage::kNone}));
88
89 // Storage class
90
91 struct StorageClassCase {
92 SpvStorageClass sc;
93 bool expect_success;
94 ast::StorageClass expected;
95 };
operator <<(std::ostream & out,StorageClassCase scc)96 inline std::ostream& operator<<(std::ostream& out, StorageClassCase scc) {
97 out << "StorageClassCase{ SpvStorageClass:" << int(scc.sc)
98 << " expect_success?:" << int(scc.expect_success)
99 << " expected:" << int(scc.expected) << "}";
100 return out;
101 }
102
103 class SpvStorageClassTest : public testing::TestWithParam<StorageClassCase> {
104 public:
SpvStorageClassTest()105 SpvStorageClassTest()
106 : success_(true),
107 fail_stream_(&success_, &errors_),
108 converter_(fail_stream_) {}
109
error() const110 std::string error() const { return errors_.str(); }
111
112 protected:
113 bool success_ = true;
114 std::stringstream errors_;
115 FailStream fail_stream_;
116 EnumConverter converter_;
117 };
118
TEST_P(SpvStorageClassTest,Samples)119 TEST_P(SpvStorageClassTest, Samples) {
120 const auto params = GetParam();
121
122 const auto result = converter_.ToStorageClass(params.sc);
123 EXPECT_EQ(success_, params.expect_success);
124 if (params.expect_success) {
125 EXPECT_EQ(result, params.expected);
126 EXPECT_TRUE(error().empty());
127 } else {
128 EXPECT_EQ(result, params.expected);
129 EXPECT_THAT(error(),
130 ::testing::StartsWith("unknown SPIR-V storage class: "));
131 }
132 }
133
134 INSTANTIATE_TEST_SUITE_P(
135 EnumConverterGood,
136 SpvStorageClassTest,
137 testing::Values(
138 StorageClassCase{SpvStorageClassInput, true, ast::StorageClass::kInput},
139 StorageClassCase{SpvStorageClassOutput, true,
140 ast::StorageClass::kOutput},
141 StorageClassCase{SpvStorageClassUniform, true,
142 ast::StorageClass::kUniform},
143 StorageClassCase{SpvStorageClassWorkgroup, true,
144 ast::StorageClass::kWorkgroup},
145 StorageClassCase{SpvStorageClassUniformConstant, true,
146 ast::StorageClass::kNone},
147 StorageClassCase{SpvStorageClassStorageBuffer, true,
148 ast::StorageClass::kStorage},
149 StorageClassCase{SpvStorageClassImage, true, ast::StorageClass::kImage},
150 StorageClassCase{SpvStorageClassPrivate, true,
151 ast::StorageClass::kPrivate},
152 StorageClassCase{SpvStorageClassFunction, true,
153 ast::StorageClass::kFunction}));
154
155 INSTANTIATE_TEST_SUITE_P(EnumConverterBad,
156 SpvStorageClassTest,
157 testing::Values(StorageClassCase{
158 static_cast<SpvStorageClass>(9999), false,
159 ast::StorageClass::kInvalid}));
160
161 // Builtin
162
163 struct BuiltinCase {
164 SpvBuiltIn builtin;
165 bool expect_success;
166 ast::Builtin expected;
167 };
operator <<(std::ostream & out,BuiltinCase bc)168 inline std::ostream& operator<<(std::ostream& out, BuiltinCase bc) {
169 out << "BuiltinCase{ SpvBuiltIn:" << int(bc.builtin)
170 << " expect_success?:" << int(bc.expect_success)
171 << " expected:" << int(bc.expected) << "}";
172 return out;
173 }
174
175 class SpvBuiltinTest : public testing::TestWithParam<BuiltinCase> {
176 public:
SpvBuiltinTest()177 SpvBuiltinTest()
178 : success_(true),
179 fail_stream_(&success_, &errors_),
180 converter_(fail_stream_) {}
181
error() const182 std::string error() const { return errors_.str(); }
183
184 protected:
185 bool success_ = true;
186 std::stringstream errors_;
187 FailStream fail_stream_;
188 EnumConverter converter_;
189 };
190
TEST_P(SpvBuiltinTest,Samples)191 TEST_P(SpvBuiltinTest, Samples) {
192 const auto params = GetParam();
193
194 const auto result = converter_.ToBuiltin(params.builtin);
195 EXPECT_EQ(success_, params.expect_success);
196 if (params.expect_success) {
197 EXPECT_EQ(result, params.expected);
198 EXPECT_TRUE(error().empty());
199 } else {
200 EXPECT_EQ(result, params.expected);
201 EXPECT_THAT(error(), ::testing::StartsWith("unknown SPIR-V builtin: "));
202 }
203 }
204
205 INSTANTIATE_TEST_SUITE_P(
206 EnumConverterGood_Input,
207 SpvBuiltinTest,
208 testing::Values(
209 BuiltinCase{SpvBuiltInPosition, true, ast::Builtin::kPosition},
210 BuiltinCase{SpvBuiltInInstanceIndex, true,
211 ast::Builtin::kInstanceIndex},
212 BuiltinCase{SpvBuiltInFrontFacing, true, ast::Builtin::kFrontFacing},
213 BuiltinCase{SpvBuiltInFragCoord, true, ast::Builtin::kPosition},
214 BuiltinCase{SpvBuiltInLocalInvocationId, true,
215 ast::Builtin::kLocalInvocationId},
216 BuiltinCase{SpvBuiltInLocalInvocationIndex, true,
217 ast::Builtin::kLocalInvocationIndex},
218 BuiltinCase{SpvBuiltInGlobalInvocationId, true,
219 ast::Builtin::kGlobalInvocationId},
220 BuiltinCase{SpvBuiltInWorkgroupId, true, ast::Builtin::kWorkgroupId},
221 BuiltinCase{SpvBuiltInSampleId, true, ast::Builtin::kSampleIndex},
222 BuiltinCase{SpvBuiltInSampleMask, true, ast::Builtin::kSampleMask}));
223
224 INSTANTIATE_TEST_SUITE_P(
225 EnumConverterGood_Output,
226 SpvBuiltinTest,
227 testing::Values(
228 BuiltinCase{SpvBuiltInPosition, true, ast::Builtin::kPosition},
229 BuiltinCase{SpvBuiltInFragDepth, true, ast::Builtin::kFragDepth},
230 BuiltinCase{SpvBuiltInSampleMask, true, ast::Builtin::kSampleMask}));
231
232 INSTANTIATE_TEST_SUITE_P(
233 EnumConverterBad,
234 SpvBuiltinTest,
235 testing::Values(
236 BuiltinCase{static_cast<SpvBuiltIn>(9999), false, ast::Builtin::kNone},
237 BuiltinCase{static_cast<SpvBuiltIn>(9999), false, ast::Builtin::kNone},
238 BuiltinCase{SpvBuiltInNumWorkgroups, false, ast::Builtin::kNone}));
239
240 // Dim
241
242 struct DimCase {
243 SpvDim dim;
244 bool arrayed;
245 bool expect_success;
246 ast::TextureDimension expected;
247 };
operator <<(std::ostream & out,DimCase dc)248 inline std::ostream& operator<<(std::ostream& out, DimCase dc) {
249 out << "DimCase{ SpvDim:" << int(dc.dim) << " arrayed?:" << int(dc.arrayed)
250 << " expect_success?:" << int(dc.expect_success)
251 << " expected:" << int(dc.expected) << "}";
252 return out;
253 }
254
255 class SpvDimTest : public testing::TestWithParam<DimCase> {
256 public:
SpvDimTest()257 SpvDimTest()
258 : success_(true),
259 fail_stream_(&success_, &errors_),
260 converter_(fail_stream_) {}
261
error() const262 std::string error() const { return errors_.str(); }
263
264 protected:
265 bool success_ = true;
266 std::stringstream errors_;
267 FailStream fail_stream_;
268 EnumConverter converter_;
269 };
270
TEST_P(SpvDimTest,Samples)271 TEST_P(SpvDimTest, Samples) {
272 const auto params = GetParam();
273
274 const auto result = converter_.ToDim(params.dim, params.arrayed);
275 EXPECT_EQ(success_, params.expect_success);
276 if (params.expect_success) {
277 EXPECT_EQ(result, params.expected);
278 EXPECT_TRUE(error().empty());
279 } else {
280 EXPECT_EQ(result, params.expected);
281 EXPECT_THAT(error(), ::testing::HasSubstr("dimension"));
282 }
283 }
284
285 INSTANTIATE_TEST_SUITE_P(
286 EnumConverterGood,
287 SpvDimTest,
288 testing::Values(
289 // Non-arrayed
290 DimCase{SpvDim1D, false, true, ast::TextureDimension::k1d},
291 DimCase{SpvDim2D, false, true, ast::TextureDimension::k2d},
292 DimCase{SpvDim3D, false, true, ast::TextureDimension::k3d},
293 DimCase{SpvDimCube, false, true, ast::TextureDimension::kCube},
294 // Arrayed
295 DimCase{SpvDim2D, true, true, ast::TextureDimension::k2dArray},
296 DimCase{SpvDimCube, true, true, ast::TextureDimension::kCubeArray}));
297
298 INSTANTIATE_TEST_SUITE_P(
299 EnumConverterBad,
300 SpvDimTest,
301 testing::Values(
302 // Invalid SPIR-V dimensionality.
303 DimCase{SpvDimMax, false, false, ast::TextureDimension::kNone},
304 DimCase{SpvDimMax, true, false, ast::TextureDimension::kNone},
305 // Vulkan non-arrayed dimensionalities not supported by WGSL.
306 DimCase{SpvDimRect, false, false, ast::TextureDimension::kNone},
307 DimCase{SpvDimBuffer, false, false, ast::TextureDimension::kNone},
308 DimCase{SpvDimSubpassData, false, false, ast::TextureDimension::kNone},
309 // Arrayed dimensionalities not supported by WGSL
310 DimCase{SpvDim3D, true, false, ast::TextureDimension::kNone},
311 DimCase{SpvDimRect, true, false, ast::TextureDimension::kNone},
312 DimCase{SpvDimBuffer, true, false, ast::TextureDimension::kNone},
313 DimCase{SpvDimSubpassData, true, false, ast::TextureDimension::kNone}));
314
315 // ImageFormat
316
317 struct ImageFormatCase {
318 SpvImageFormat format;
319 bool expect_success;
320 ast::ImageFormat expected;
321 };
operator <<(std::ostream & out,ImageFormatCase ifc)322 inline std::ostream& operator<<(std::ostream& out, ImageFormatCase ifc) {
323 out << "ImageFormatCase{ SpvImageFormat:" << int(ifc.format)
324 << " expect_success?:" << int(ifc.expect_success)
325 << " expected:" << int(ifc.expected) << "}";
326 return out;
327 }
328
329 class SpvImageFormatTest : public testing::TestWithParam<ImageFormatCase> {
330 public:
SpvImageFormatTest()331 SpvImageFormatTest()
332 : success_(true),
333 fail_stream_(&success_, &errors_),
334 converter_(fail_stream_) {}
335
error() const336 std::string error() const { return errors_.str(); }
337
338 protected:
339 bool success_ = true;
340 std::stringstream errors_;
341 FailStream fail_stream_;
342 EnumConverter converter_;
343 };
344
TEST_P(SpvImageFormatTest,Samples)345 TEST_P(SpvImageFormatTest, Samples) {
346 const auto params = GetParam();
347
348 const auto result = converter_.ToImageFormat(params.format);
349 EXPECT_EQ(success_, params.expect_success) << params;
350 if (params.expect_success) {
351 EXPECT_EQ(result, params.expected);
352 EXPECT_TRUE(error().empty());
353 } else {
354 EXPECT_EQ(result, params.expected);
355 EXPECT_THAT(error(), ::testing::StartsWith("invalid image format: "));
356 }
357 }
358
359 INSTANTIATE_TEST_SUITE_P(
360 EnumConverterGood,
361 SpvImageFormatTest,
362 testing::Values(
363 // Unknown. This is used for sampled images.
364 ImageFormatCase{SpvImageFormatUnknown, true, ast::ImageFormat::kNone},
365 // 8 bit channels
366 ImageFormatCase{SpvImageFormatRgba8, true,
367 ast::ImageFormat::kRgba8Unorm},
368 ImageFormatCase{SpvImageFormatRgba8Snorm, true,
369 ast::ImageFormat::kRgba8Snorm},
370 ImageFormatCase{SpvImageFormatRgba8ui, true,
371 ast::ImageFormat::kRgba8Uint},
372 ImageFormatCase{SpvImageFormatRgba8i, true,
373 ast::ImageFormat::kRgba8Sint},
374 // 16 bit channels
375 ImageFormatCase{SpvImageFormatRgba16ui, true,
376 ast::ImageFormat::kRgba16Uint},
377 ImageFormatCase{SpvImageFormatRgba16i, true,
378 ast::ImageFormat::kRgba16Sint},
379 ImageFormatCase{SpvImageFormatRgba16f, true,
380 ast::ImageFormat::kRgba16Float},
381 // 32 bit channels
382 // ... 1 channel
383 ImageFormatCase{SpvImageFormatR32ui, true, ast::ImageFormat::kR32Uint},
384 ImageFormatCase{SpvImageFormatR32i, true, ast::ImageFormat::kR32Sint},
385 ImageFormatCase{SpvImageFormatR32f, true, ast::ImageFormat::kR32Float},
386 // ... 2 channels
387 ImageFormatCase{SpvImageFormatRg32ui, true,
388 ast::ImageFormat::kRg32Uint},
389 ImageFormatCase{SpvImageFormatRg32i, true, ast::ImageFormat::kRg32Sint},
390 ImageFormatCase{SpvImageFormatRg32f, true,
391 ast::ImageFormat::kRg32Float},
392 // ... 4 channels
393 ImageFormatCase{SpvImageFormatRgba32ui, true,
394 ast::ImageFormat::kRgba32Uint},
395 ImageFormatCase{SpvImageFormatRgba32i, true,
396 ast::ImageFormat::kRgba32Sint},
397 ImageFormatCase{SpvImageFormatRgba32f, true,
398 ast::ImageFormat::kRgba32Float}));
399
400 INSTANTIATE_TEST_SUITE_P(
401 EnumConverterBad,
402 SpvImageFormatTest,
403 testing::Values(
404 // Scanning in order from the SPIR-V spec.
405 ImageFormatCase{SpvImageFormatRg16f, false, ast::ImageFormat::kNone},
406 ImageFormatCase{SpvImageFormatR11fG11fB10f, false,
407 ast::ImageFormat::kNone},
408 ImageFormatCase{SpvImageFormatR16f, false, ast::ImageFormat::kNone},
409 ImageFormatCase{SpvImageFormatRgb10A2, false, ast::ImageFormat::kNone},
410 ImageFormatCase{SpvImageFormatRg16, false, ast::ImageFormat::kNone},
411 ImageFormatCase{SpvImageFormatRg8, false, ast::ImageFormat::kNone},
412 ImageFormatCase{SpvImageFormatR16, false, ast::ImageFormat::kNone},
413 ImageFormatCase{SpvImageFormatR8, false, ast::ImageFormat::kNone},
414 ImageFormatCase{SpvImageFormatRgba16Snorm, false,
415 ast::ImageFormat::kNone},
416 ImageFormatCase{SpvImageFormatRg16Snorm, false,
417 ast::ImageFormat::kNone},
418 ImageFormatCase{SpvImageFormatRg8Snorm, false, ast::ImageFormat::kNone},
419 ImageFormatCase{SpvImageFormatRg16i, false, ast::ImageFormat::kNone},
420 ImageFormatCase{SpvImageFormatRg8i, false, ast::ImageFormat::kNone},
421 ImageFormatCase{SpvImageFormatR8i, false, ast::ImageFormat::kNone},
422 ImageFormatCase{SpvImageFormatRgb10a2ui, false,
423 ast::ImageFormat::kNone},
424 ImageFormatCase{SpvImageFormatRg16ui, false, ast::ImageFormat::kNone},
425 ImageFormatCase{SpvImageFormatRg8ui, false, ast::ImageFormat::kNone}));
426
427 } // namespace
428 } // namespace spirv
429 } // namespace reader
430 } // namespace tint
431