1 // Copyright 2021 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 "gmock/gmock.h"
16 #include "src/ast/call_statement.h"
17 #include "src/ast/stage_decoration.h"
18 #include "src/sem/call.h"
19 #include "src/writer/glsl/test_helper.h"
20
21 namespace tint {
22 namespace writer {
23 namespace glsl {
24 namespace {
25
26 using IntrinsicType = sem::IntrinsicType;
27
28 using ::testing::HasSubstr;
29
30 using GlslGeneratorImplTest_Intrinsic = TestHelper;
31
32 enum class ParamType {
33 kF32,
34 kU32,
35 kBool,
36 };
37
38 struct IntrinsicData {
39 IntrinsicType intrinsic;
40 ParamType type;
41 const char* glsl_name;
42 };
operator <<(std::ostream & out,IntrinsicData data)43 inline std::ostream& operator<<(std::ostream& out, IntrinsicData data) {
44 out << data.glsl_name;
45 switch (data.type) {
46 case ParamType::kF32:
47 out << "f32";
48 break;
49 case ParamType::kU32:
50 out << "u32";
51 break;
52 case ParamType::kBool:
53 out << "bool";
54 break;
55 }
56 out << ">";
57 return out;
58 }
59
GenerateCall(IntrinsicType intrinsic,ParamType type,ProgramBuilder * builder)60 const ast::CallExpression* GenerateCall(IntrinsicType intrinsic,
61 ParamType type,
62 ProgramBuilder* builder) {
63 std::string name;
64 std::ostringstream str(name);
65 str << intrinsic;
66 switch (intrinsic) {
67 case IntrinsicType::kAcos:
68 case IntrinsicType::kAsin:
69 case IntrinsicType::kAtan:
70 case IntrinsicType::kCeil:
71 case IntrinsicType::kCos:
72 case IntrinsicType::kCosh:
73 case IntrinsicType::kDpdx:
74 case IntrinsicType::kDpdxCoarse:
75 case IntrinsicType::kDpdxFine:
76 case IntrinsicType::kDpdy:
77 case IntrinsicType::kDpdyCoarse:
78 case IntrinsicType::kDpdyFine:
79 case IntrinsicType::kExp:
80 case IntrinsicType::kExp2:
81 case IntrinsicType::kFloor:
82 case IntrinsicType::kFract:
83 case IntrinsicType::kFwidth:
84 case IntrinsicType::kFwidthCoarse:
85 case IntrinsicType::kFwidthFine:
86 case IntrinsicType::kInverseSqrt:
87 case IntrinsicType::kIsFinite:
88 case IntrinsicType::kIsInf:
89 case IntrinsicType::kIsNan:
90 case IntrinsicType::kIsNormal:
91 case IntrinsicType::kLength:
92 case IntrinsicType::kLog:
93 case IntrinsicType::kLog2:
94 case IntrinsicType::kNormalize:
95 case IntrinsicType::kRound:
96 case IntrinsicType::kSin:
97 case IntrinsicType::kSinh:
98 case IntrinsicType::kSqrt:
99 case IntrinsicType::kTan:
100 case IntrinsicType::kTanh:
101 case IntrinsicType::kTrunc:
102 case IntrinsicType::kSign:
103 return builder->Call(str.str(), "f2");
104 case IntrinsicType::kLdexp:
105 return builder->Call(str.str(), "f2", "i2");
106 case IntrinsicType::kAtan2:
107 case IntrinsicType::kDot:
108 case IntrinsicType::kDistance:
109 case IntrinsicType::kPow:
110 case IntrinsicType::kReflect:
111 case IntrinsicType::kStep:
112 return builder->Call(str.str(), "f2", "f2");
113 case IntrinsicType::kCross:
114 return builder->Call(str.str(), "f3", "f3");
115 case IntrinsicType::kFma:
116 case IntrinsicType::kMix:
117 case IntrinsicType::kFaceForward:
118 case IntrinsicType::kSmoothStep:
119 return builder->Call(str.str(), "f2", "f2", "f2");
120 case IntrinsicType::kAll:
121 case IntrinsicType::kAny:
122 return builder->Call(str.str(), "b2");
123 case IntrinsicType::kAbs:
124 if (type == ParamType::kF32) {
125 return builder->Call(str.str(), "f2");
126 } else {
127 return builder->Call(str.str(), "u2");
128 }
129 case IntrinsicType::kCountOneBits:
130 case IntrinsicType::kReverseBits:
131 return builder->Call(str.str(), "u2");
132 case IntrinsicType::kMax:
133 case IntrinsicType::kMin:
134 if (type == ParamType::kF32) {
135 return builder->Call(str.str(), "f2", "f2");
136 } else {
137 return builder->Call(str.str(), "u2", "u2");
138 }
139 case IntrinsicType::kClamp:
140 if (type == ParamType::kF32) {
141 return builder->Call(str.str(), "f2", "f2", "f2");
142 } else {
143 return builder->Call(str.str(), "u2", "u2", "u2");
144 }
145 case IntrinsicType::kSelect:
146 return builder->Call(str.str(), "f2", "f2", "b2");
147 case IntrinsicType::kDeterminant:
148 return builder->Call(str.str(), "m2x2");
149 case IntrinsicType::kTranspose:
150 return builder->Call(str.str(), "m3x2");
151 default:
152 break;
153 }
154 return nullptr;
155 }
156 using GlslIntrinsicTest = TestParamHelper<IntrinsicData>;
TEST_P(GlslIntrinsicTest,Emit)157 TEST_P(GlslIntrinsicTest, Emit) {
158 auto param = GetParam();
159
160 Global("f2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
161 Global("f3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
162 Global("u2", ty.vec2<u32>(), ast::StorageClass::kPrivate);
163 Global("i2", ty.vec2<i32>(), ast::StorageClass::kPrivate);
164 Global("b2", ty.vec2<bool>(), ast::StorageClass::kPrivate);
165 Global("m2x2", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
166 Global("m3x2", ty.mat3x2<f32>(), ast::StorageClass::kPrivate);
167
168 auto* call = GenerateCall(param.intrinsic, param.type, this);
169 ASSERT_NE(nullptr, call) << "Unhandled intrinsic";
170 Func("func", {}, ty.void_(), {CallStmt(call)},
171 {create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
172
173 GeneratorImpl& gen = Build();
174
175 auto* sem = program->Sem().Get(call);
176 ASSERT_NE(sem, nullptr);
177 auto* target = sem->Target();
178 ASSERT_NE(target, nullptr);
179 auto* intrinsic = target->As<sem::Intrinsic>();
180 ASSERT_NE(intrinsic, nullptr);
181
182 EXPECT_EQ(gen.generate_builtin_name(intrinsic), param.glsl_name);
183 }
184 INSTANTIATE_TEST_SUITE_P(
185 GlslGeneratorImplTest_Intrinsic,
186 GlslIntrinsicTest,
187 testing::Values(
188 IntrinsicData{IntrinsicType::kAbs, ParamType::kF32, "abs"},
189 IntrinsicData{IntrinsicType::kAbs, ParamType::kU32, "abs"},
190 IntrinsicData{IntrinsicType::kAcos, ParamType::kF32, "acos"},
191 IntrinsicData{IntrinsicType::kAll, ParamType::kBool, "all"},
192 IntrinsicData{IntrinsicType::kAny, ParamType::kBool, "any"},
193 IntrinsicData{IntrinsicType::kAsin, ParamType::kF32, "asin"},
194 IntrinsicData{IntrinsicType::kAtan, ParamType::kF32, "atan"},
195 IntrinsicData{IntrinsicType::kAtan2, ParamType::kF32, "atan"},
196 IntrinsicData{IntrinsicType::kCeil, ParamType::kF32, "ceil"},
197 IntrinsicData{IntrinsicType::kClamp, ParamType::kF32, "clamp"},
198 IntrinsicData{IntrinsicType::kClamp, ParamType::kU32, "clamp"},
199 IntrinsicData{IntrinsicType::kCos, ParamType::kF32, "cos"},
200 IntrinsicData{IntrinsicType::kCosh, ParamType::kF32, "cosh"},
201 IntrinsicData{IntrinsicType::kCountOneBits, ParamType::kU32,
202 "countbits"},
203 IntrinsicData{IntrinsicType::kCross, ParamType::kF32, "cross"},
204 IntrinsicData{IntrinsicType::kDeterminant, ParamType::kF32,
205 "determinant"},
206 IntrinsicData{IntrinsicType::kDistance, ParamType::kF32, "distance"},
207 IntrinsicData{IntrinsicType::kDot, ParamType::kF32, "dot"},
208 IntrinsicData{IntrinsicType::kDpdx, ParamType::kF32, "ddx"},
209 IntrinsicData{IntrinsicType::kDpdxCoarse, ParamType::kF32,
210 "ddx_coarse"},
211 IntrinsicData{IntrinsicType::kDpdxFine, ParamType::kF32, "ddx_fine"},
212 IntrinsicData{IntrinsicType::kDpdy, ParamType::kF32, "ddy"},
213 IntrinsicData{IntrinsicType::kDpdyCoarse, ParamType::kF32,
214 "ddy_coarse"},
215 IntrinsicData{IntrinsicType::kDpdyFine, ParamType::kF32, "ddy_fine"},
216 IntrinsicData{IntrinsicType::kExp, ParamType::kF32, "exp"},
217 IntrinsicData{IntrinsicType::kExp2, ParamType::kF32, "exp2"},
218 IntrinsicData{IntrinsicType::kFaceForward, ParamType::kF32,
219 "faceforward"},
220 IntrinsicData{IntrinsicType::kFloor, ParamType::kF32, "floor"},
221 IntrinsicData{IntrinsicType::kFma, ParamType::kF32, "mad"},
222 IntrinsicData{IntrinsicType::kFract, ParamType::kF32, "frac"},
223 IntrinsicData{IntrinsicType::kFwidth, ParamType::kF32, "fwidth"},
224 IntrinsicData{IntrinsicType::kFwidthCoarse, ParamType::kF32, "fwidth"},
225 IntrinsicData{IntrinsicType::kFwidthFine, ParamType::kF32, "fwidth"},
226 IntrinsicData{IntrinsicType::kInverseSqrt, ParamType::kF32, "rsqrt"},
227 IntrinsicData{IntrinsicType::kIsFinite, ParamType::kF32, "isfinite"},
228 IntrinsicData{IntrinsicType::kIsInf, ParamType::kF32, "isinf"},
229 IntrinsicData{IntrinsicType::kIsNan, ParamType::kF32, "isnan"},
230 IntrinsicData{IntrinsicType::kLdexp, ParamType::kF32, "ldexp"},
231 IntrinsicData{IntrinsicType::kLength, ParamType::kF32, "length"},
232 IntrinsicData{IntrinsicType::kLog, ParamType::kF32, "log"},
233 IntrinsicData{IntrinsicType::kLog2, ParamType::kF32, "log2"},
234 IntrinsicData{IntrinsicType::kMax, ParamType::kF32, "max"},
235 IntrinsicData{IntrinsicType::kMax, ParamType::kU32, "max"},
236 IntrinsicData{IntrinsicType::kMin, ParamType::kF32, "min"},
237 IntrinsicData{IntrinsicType::kMin, ParamType::kU32, "min"},
238 IntrinsicData{IntrinsicType::kMix, ParamType::kF32, "mix"},
239 IntrinsicData{IntrinsicType::kNormalize, ParamType::kF32, "normalize"},
240 IntrinsicData{IntrinsicType::kPow, ParamType::kF32, "pow"},
241 IntrinsicData{IntrinsicType::kReflect, ParamType::kF32, "reflect"},
242 IntrinsicData{IntrinsicType::kReverseBits, ParamType::kU32,
243 "reversebits"},
244 IntrinsicData{IntrinsicType::kRound, ParamType::kU32, "round"},
245 IntrinsicData{IntrinsicType::kSign, ParamType::kF32, "sign"},
246 IntrinsicData{IntrinsicType::kSin, ParamType::kF32, "sin"},
247 IntrinsicData{IntrinsicType::kSinh, ParamType::kF32, "sinh"},
248 IntrinsicData{IntrinsicType::kSmoothStep, ParamType::kF32,
249 "smoothstep"},
250 IntrinsicData{IntrinsicType::kSqrt, ParamType::kF32, "sqrt"},
251 IntrinsicData{IntrinsicType::kStep, ParamType::kF32, "step"},
252 IntrinsicData{IntrinsicType::kTan, ParamType::kF32, "tan"},
253 IntrinsicData{IntrinsicType::kTanh, ParamType::kF32, "tanh"},
254 IntrinsicData{IntrinsicType::kTranspose, ParamType::kF32, "transpose"},
255 IntrinsicData{IntrinsicType::kTrunc, ParamType::kF32, "trunc"}));
256
TEST_F(GlslGeneratorImplTest_Intrinsic,DISABLED_Intrinsic_IsNormal)257 TEST_F(GlslGeneratorImplTest_Intrinsic, DISABLED_Intrinsic_IsNormal) {
258 FAIL();
259 }
260
TEST_F(GlslGeneratorImplTest_Intrinsic,Intrinsic_Call)261 TEST_F(GlslGeneratorImplTest_Intrinsic, Intrinsic_Call) {
262 auto* call = Call("dot", "param1", "param2");
263
264 Global("param1", ty.vec3<f32>(), ast::StorageClass::kPrivate);
265 Global("param2", ty.vec3<f32>(), ast::StorageClass::kPrivate);
266
267 WrapInFunction(CallStmt(call));
268
269 GeneratorImpl& gen = Build();
270
271 gen.increment_indent();
272 std::stringstream out;
273 ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
274 EXPECT_EQ(out.str(), "dot(param1, param2)");
275 }
276
TEST_F(GlslGeneratorImplTest_Intrinsic,Select_Scalar)277 TEST_F(GlslGeneratorImplTest_Intrinsic, Select_Scalar) {
278 auto* call = Call("select", 1.0f, 2.0f, true);
279 WrapInFunction(CallStmt(call));
280 GeneratorImpl& gen = Build();
281
282 gen.increment_indent();
283 std::stringstream out;
284 ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
285 EXPECT_EQ(out.str(), "(true ? 2.0f : 1.0f)");
286 }
287
TEST_F(GlslGeneratorImplTest_Intrinsic,Select_Vector)288 TEST_F(GlslGeneratorImplTest_Intrinsic, Select_Vector) {
289 auto* call =
290 Call("select", vec2<i32>(1, 2), vec2<i32>(3, 4), vec2<bool>(true, false));
291 WrapInFunction(CallStmt(call));
292 GeneratorImpl& gen = Build();
293
294 gen.increment_indent();
295 std::stringstream out;
296 ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
297 EXPECT_EQ(out.str(), "(bvec2(true, false) ? ivec2(3, 4) : ivec2(1, 2))");
298 }
299
300 #if 0
301 TEST_F(GlslGeneratorImplTest_Intrinsic, Modf_Scalar) {
302 auto* res = Var("res", ty.f32());
303 auto* call = Call("modf", 1.0f, AddressOf(res));
304 WrapInFunction(res, call);
305
306 GeneratorImpl& gen = SanitizeAndBuild();
307
308 ASSERT_TRUE(gen.Generate()) << gen.error();
309 EXPECT_THAT(gen.result(), HasSubstr("modf(1.0f, res)"));
310 }
311
312 TEST_F(GlslGeneratorImplTest_Intrinsic, Modf_Vector) {
313 auto* res = Var("res", ty.vec3<f32>());
314 auto* call = Call("modf", vec3<f32>(), AddressOf(res));
315 WrapInFunction(res, call);
316
317 GeneratorImpl& gen = SanitizeAndBuild();
318
319 ASSERT_TRUE(gen.Generate()) << gen.error();
320 EXPECT_THAT(gen.result(), HasSubstr("modf(vec3(0.0f, 0.0f, 0.0f), res)"));
321 }
322
323 TEST_F(GlslGeneratorImplTest_Intrinsic, Frexp_Scalar_i32) {
324 auto* exp = Var("exp", ty.i32());
325 auto* call = Call("frexp", 1.0f, AddressOf(exp));
326 WrapInFunction(exp, call);
327
328 GeneratorImpl& gen = SanitizeAndBuild();
329
330 ASSERT_TRUE(gen.Generate()) << gen.error();
331 EXPECT_THAT(gen.result(), HasSubstr(R"(
332 float tint_tmp;
333 float tint_tmp_1 = frexp(1.0f, tint_tmp);
334 exp = int(tint_tmp);
335 tint_tmp_1;
336 )"));
337 }
338
339 TEST_F(GlslGeneratorImplTest_Intrinsic, Frexp_Vector_i32) {
340 auto* res = Var("res", ty.vec3<i32>());
341 auto* call = Call("frexp", vec3<f32>(), AddressOf(res));
342 WrapInFunction(res, call);
343
344 GeneratorImpl& gen = SanitizeAndBuild();
345
346 ASSERT_TRUE(gen.Generate()) << gen.error();
347 EXPECT_THAT(gen.result(), HasSubstr(R"(
348 vec3 tint_tmp;
349 vec3 tint_tmp_1 = frexp(vec3(0.0f, 0.0f, 0.0f), tint_tmp);
350 res = ivec3(tint_tmp);
351 tint_tmp_1;
352 )"));
353 }
354
355 TEST_F(GlslGeneratorImplTest_Intrinsic, IsNormal_Scalar) {
356 auto* val = Var("val", ty.f32());
357 auto* call = Call("isNormal", val);
358 WrapInFunction(val, call);
359
360 GeneratorImpl& gen = SanitizeAndBuild();
361
362 ASSERT_TRUE(gen.Generate()) << gen.error();
363 EXPECT_THAT(gen.result(), HasSubstr(R"(
364 uint tint_isnormal_exponent = asuint(val) & 0x7f80000;
365 uint tint_isnormal_clamped = clamp(tint_isnormal_exponent, 0x0080000, 0x7f00000);
366 (tint_isnormal_clamped == tint_isnormal_exponent);
367 )"));
368 }
369
370 TEST_F(GlslGeneratorImplTest_Intrinsic, IsNormal_Vector) {
371 auto* val = Var("val", ty.vec3<f32>());
372 auto* call = Call("isNormal", val);
373 WrapInFunction(val, call);
374
375 GeneratorImpl& gen = SanitizeAndBuild();
376
377 ASSERT_TRUE(gen.Generate()) << gen.error();
378 EXPECT_THAT(gen.result(), HasSubstr(R"(
379 uvec3 tint_isnormal_exponent = asuint(val) & 0x7f80000;
380 uvec3 tint_isnormal_clamped = clamp(tint_isnormal_exponent, 0x0080000, 0x7f00000);
381 (tint_isnormal_clamped == tint_isnormal_exponent);
382 )"));
383 }
384
385 TEST_F(GlslGeneratorImplTest_Intrinsic, Pack4x8Snorm) {
386 auto* call = Call("pack4x8snorm", "p1");
387 Global("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
388 WrapInFunction(CallStmt(call));
389 GeneratorImpl& gen = Build();
390
391 gen.increment_indent();
392 std::stringstream out;
393 ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
394 EXPECT_THAT(gen.result(), HasSubstr("ivec4 tint_tmp = ivec4(round(clamp(p1, "
395 "-1.0, 1.0) * 127.0)) & 0xff;"));
396 EXPECT_THAT(out.str(), HasSubstr("asuint(tint_tmp.x | tint_tmp.y << 8 | "
397 "tint_tmp.z << 16 | tint_tmp.w << 24)"));
398 }
399
400 TEST_F(GlslGeneratorImplTest_Intrinsic, Pack4x8Unorm) {
401 auto* call = Call("pack4x8unorm", "p1");
402 Global("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
403 WrapInFunction(CallStmt(call));
404 GeneratorImpl& gen = Build();
405
406 gen.increment_indent();
407 std::stringstream out;
408 ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
409 EXPECT_THAT(gen.result(), HasSubstr("uvec4 tint_tmp = uvec4(round(clamp(p1, "
410 "0.0, 1.0) * 255.0));"));
411 EXPECT_THAT(out.str(), HasSubstr("(tint_tmp.x | tint_tmp.y << 8 | "
412 "tint_tmp.z << 16 | tint_tmp.w << 24)"));
413 }
414
415 TEST_F(GlslGeneratorImplTest_Intrinsic, Pack2x16Snorm) {
416 auto* call = Call("pack2x16snorm", "p1");
417 Global("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
418 WrapInFunction(CallStmt(call));
419 GeneratorImpl& gen = Build();
420
421 gen.increment_indent();
422 std::stringstream out;
423 ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
424 EXPECT_THAT(gen.result(), HasSubstr("int2 tint_tmp = int2(round(clamp(p1, "
425 "-1.0, 1.0) * 32767.0)) & 0xffff;"));
426 EXPECT_THAT(out.str(), HasSubstr("asuint(tint_tmp.x | tint_tmp.y << 16)"));
427 }
428
429 TEST_F(GlslGeneratorImplTest_Intrinsic, Pack2x16Unorm) {
430 auto* call = Call("pack2x16unorm", "p1");
431 Global("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
432 WrapInFunction(CallStmt(call));
433 GeneratorImpl& gen = Build();
434
435 gen.increment_indent();
436 std::stringstream out;
437 ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
438 EXPECT_THAT(gen.result(), HasSubstr("uint2 tint_tmp = uint2(round(clamp(p1, "
439 "0.0, 1.0) * 65535.0));"));
440 EXPECT_THAT(out.str(), HasSubstr("(tint_tmp.x | tint_tmp.y << 16)"));
441 }
442
443 TEST_F(GlslGeneratorImplTest_Intrinsic, Pack2x16Float) {
444 auto* call = Call("pack2x16float", "p1");
445 Global("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
446 WrapInFunction(CallStmt(call));
447 GeneratorImpl& gen = Build();
448
449 gen.increment_indent();
450 std::stringstream out;
451 ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
452 EXPECT_THAT(gen.result(), HasSubstr("uint2 tint_tmp = f32tof16(p1);"));
453 EXPECT_THAT(out.str(), HasSubstr("(tint_tmp.x | tint_tmp.y << 16)"));
454 }
455
456 TEST_F(GlslGeneratorImplTest_Intrinsic, Unpack4x8Snorm) {
457 auto* call = Call("unpack4x8snorm", "p1");
458 Global("p1", ty.u32(), ast::StorageClass::kPrivate);
459 WrapInFunction(CallStmt(call));
460 GeneratorImpl& gen = Build();
461
462 gen.increment_indent();
463 std::stringstream out;
464 ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
465 EXPECT_THAT(gen.result(), HasSubstr("int tint_tmp_1 = int(p1);"));
466 EXPECT_THAT(gen.result(),
467 HasSubstr("ivec4 tint_tmp = ivec4(tint_tmp_1 << 24, tint_tmp_1 "
468 "<< 16, tint_tmp_1 << 8, tint_tmp_1) >> 24;"));
469 EXPECT_THAT(out.str(),
470 HasSubstr("clamp(float4(tint_tmp) / 127.0, -1.0, 1.0)"));
471 }
472
473 TEST_F(GlslGeneratorImplTest_Intrinsic, Unpack4x8Unorm) {
474 auto* call = Call("unpack4x8unorm", "p1");
475 Global("p1", ty.u32(), ast::StorageClass::kPrivate);
476 WrapInFunction(CallStmt(call));
477 GeneratorImpl& gen = Build();
478
479 gen.increment_indent();
480 std::stringstream out;
481 ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
482 EXPECT_THAT(gen.result(), HasSubstr("uint tint_tmp_1 = p1;"));
483 EXPECT_THAT(
484 gen.result(),
485 HasSubstr("uvec4 tint_tmp = uvec4(tint_tmp_1 & 0xff, (tint_tmp_1 >> "
486 "8) & 0xff, (tint_tmp_1 >> 16) & 0xff, tint_tmp_1 >> 24);"));
487 EXPECT_THAT(out.str(), HasSubstr("float4(tint_tmp) / 255.0"));
488 }
489
490 TEST_F(GlslGeneratorImplTest_Intrinsic, Unpack2x16Snorm) {
491 auto* call = Call("unpack2x16snorm", "p1");
492 Global("p1", ty.u32(), ast::StorageClass::kPrivate);
493 WrapInFunction(CallStmt(call));
494 GeneratorImpl& gen = Build();
495
496 gen.increment_indent();
497 std::stringstream out;
498 ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
499 EXPECT_THAT(gen.result(), HasSubstr("int tint_tmp_1 = int(p1);"));
500 EXPECT_THAT(
501 gen.result(),
502 HasSubstr("int2 tint_tmp = int2(tint_tmp_1 << 16, tint_tmp_1) >> 16;"));
503 EXPECT_THAT(out.str(),
504 HasSubstr("clamp(float2(tint_tmp) / 32767.0, -1.0, 1.0)"));
505 }
506
507 TEST_F(GlslGeneratorImplTest_Intrinsic, Unpack2x16Unorm) {
508 auto* call = Call("unpack2x16unorm", "p1");
509 Global("p1", ty.u32(), ast::StorageClass::kPrivate);
510 WrapInFunction(CallStmt(call));
511 GeneratorImpl& gen = Build();
512
513 gen.increment_indent();
514 std::stringstream out;
515 ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
516 EXPECT_THAT(gen.result(), HasSubstr("uint tint_tmp_1 = p1;"));
517 EXPECT_THAT(gen.result(),
518 HasSubstr("uint2 tint_tmp = uint2(tint_tmp_1 & 0xffff, "
519 "tint_tmp_1 >> 16);"));
520 EXPECT_THAT(out.str(), HasSubstr("float2(tint_tmp) / 65535.0"));
521 }
522
523 TEST_F(GlslGeneratorImplTest_Intrinsic, Unpack2x16Float) {
524 auto* call = Call("unpack2x16float", "p1");
525 Global("p1", ty.u32(), ast::StorageClass::kPrivate);
526 WrapInFunction(CallStmt(call));
527 GeneratorImpl& gen = Build();
528
529 gen.increment_indent();
530 std::stringstream out;
531 ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
532 EXPECT_THAT(gen.result(), HasSubstr("uint tint_tmp = p1;"));
533 EXPECT_THAT(out.str(),
534 HasSubstr("f16tof32(uint2(tint_tmp & 0xffff, tint_tmp >> 16))"));
535 }
536
537 TEST_F(GlslGeneratorImplTest_Intrinsic, StorageBarrier) {
538 Func("main", {}, ty.void_(),
539 {CallStmt(Call("storageBarrier"))},
540 {
541 Stage(ast::PipelineStage::kCompute),
542 WorkgroupSize(1),
543 });
544
545 GeneratorImpl& gen = Build();
546
547 ASSERT_TRUE(gen.Generate()) << gen.error();
548 EXPECT_EQ(gen.result(), R"(layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
549 void main() {
550 DeviceMemoryBarrierWithGroupSync();
551 return;
552 }
553 )");
554 }
555
556 TEST_F(GlslGeneratorImplTest_Intrinsic, WorkgroupBarrier) {
557 Func("main", {}, ty.void_(),
558 {CallStmt(Call("workgroupBarrier"))},
559 {
560 Stage(ast::PipelineStage::kCompute),
561 WorkgroupSize(1),
562 });
563
564 GeneratorImpl& gen = Build();
565
566 ASSERT_TRUE(gen.Generate()) << gen.error();
567 EXPECT_EQ(gen.result(), R"(layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
568 void main() {
569 GroupMemoryBarrierWithGroupSync();
570 return;
571 }
572 )");
573 }
574
575 TEST_F(GlslGeneratorImplTest_Intrinsic, Ignore) {
576 Func("f", {Param("a", ty.i32()), Param("b", ty.i32()), Param("c", ty.i32())},
577 ty.i32(), {Return(Mul(Add("a", "b"), "c"))});
578
579 Func("main", {}, ty.void_(),
580 {CallStmt(Call("ignore", Call("f", 1, 2, 3)))},
581 {
582 Stage(ast::PipelineStage::kCompute),
583 WorkgroupSize(1),
584 });
585
586 GeneratorImpl& gen = Build();
587
588 ASSERT_TRUE(gen.Generate()) << gen.error();
589 EXPECT_EQ(gen.result(), R"(int f(int a, int b, int c) {
590 return ((a + b) * c);
591 }
592
593 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
594 void main() {
595 f(1, 2, 3);
596 return;
597 }
598 )");
599 }
600 #endif
601
TEST_F(GlslGeneratorImplTest_Intrinsic,DotI32)602 TEST_F(GlslGeneratorImplTest_Intrinsic, DotI32) {
603 Global("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
604 WrapInFunction(CallStmt(Call("dot", "v", "v")));
605
606 GeneratorImpl& gen = SanitizeAndBuild();
607
608 ASSERT_TRUE(gen.Generate()) << gen.error();
609 EXPECT_EQ(gen.result(), R"(#version 310 es
610 precision mediump float;
611
612 int tint_int_dot(ivec3 a, ivec3 b) {
613 return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
614 }
615
616 ivec3 v = ivec3(0, 0, 0);
617
618 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
619 void test_function() {
620 tint_int_dot(v, v);
621 return;
622 }
623 void main() {
624 test_function();
625 }
626
627
628 )");
629 }
630
TEST_F(GlslGeneratorImplTest_Intrinsic,DotU32)631 TEST_F(GlslGeneratorImplTest_Intrinsic, DotU32) {
632 Global("v", ty.vec3<u32>(), ast::StorageClass::kPrivate);
633 WrapInFunction(CallStmt(Call("dot", "v", "v")));
634
635 GeneratorImpl& gen = SanitizeAndBuild();
636
637 ASSERT_TRUE(gen.Generate()) << gen.error();
638 EXPECT_EQ(gen.result(), R"(#version 310 es
639 precision mediump float;
640
641 uint tint_int_dot(uvec3 a, uvec3 b) {
642 return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
643 }
644
645 uvec3 v = uvec3(0u, 0u, 0u);
646
647 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
648 void test_function() {
649 tint_int_dot(v, v);
650 return;
651 }
652 void main() {
653 test_function();
654 }
655
656
657 )");
658 }
659
660 } // namespace
661 } // namespace glsl
662 } // namespace writer
663 } // namespace tint
664