• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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