blob: 102f7c378bfc7c03c04f7a5e62f4e2c8e9430907 [file] [log] [blame]
// Copyright 2021 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 "gmock/gmock.h"
#include "src/ast/call_statement.h"
#include "src/ast/stage_decoration.h"
#include "src/sem/call.h"
#include "src/writer/glsl/test_helper.h"
namespace tint {
namespace writer {
namespace glsl {
namespace {
using IntrinsicType = sem::IntrinsicType;
using ::testing::HasSubstr;
using GlslGeneratorImplTest_Intrinsic = TestHelper;
enum class ParamType {
kF32,
kU32,
kBool,
};
struct IntrinsicData {
IntrinsicType intrinsic;
ParamType type;
const char* glsl_name;
};
inline std::ostream& operator<<(std::ostream& out, IntrinsicData data) {
out << data.glsl_name;
switch (data.type) {
case ParamType::kF32:
out << "f32";
break;
case ParamType::kU32:
out << "u32";
break;
case ParamType::kBool:
out << "bool";
break;
}
out << ">";
return out;
}
const ast::CallExpression* GenerateCall(IntrinsicType intrinsic,
ParamType type,
ProgramBuilder* builder) {
std::string name;
std::ostringstream str(name);
str << intrinsic;
switch (intrinsic) {
case IntrinsicType::kAcos:
case IntrinsicType::kAsin:
case IntrinsicType::kAtan:
case IntrinsicType::kCeil:
case IntrinsicType::kCos:
case IntrinsicType::kCosh:
case IntrinsicType::kDpdx:
case IntrinsicType::kDpdxCoarse:
case IntrinsicType::kDpdxFine:
case IntrinsicType::kDpdy:
case IntrinsicType::kDpdyCoarse:
case IntrinsicType::kDpdyFine:
case IntrinsicType::kExp:
case IntrinsicType::kExp2:
case IntrinsicType::kFloor:
case IntrinsicType::kFract:
case IntrinsicType::kFwidth:
case IntrinsicType::kFwidthCoarse:
case IntrinsicType::kFwidthFine:
case IntrinsicType::kInverseSqrt:
case IntrinsicType::kIsFinite:
case IntrinsicType::kIsInf:
case IntrinsicType::kIsNan:
case IntrinsicType::kIsNormal:
case IntrinsicType::kLength:
case IntrinsicType::kLog:
case IntrinsicType::kLog2:
case IntrinsicType::kNormalize:
case IntrinsicType::kRound:
case IntrinsicType::kSin:
case IntrinsicType::kSinh:
case IntrinsicType::kSqrt:
case IntrinsicType::kTan:
case IntrinsicType::kTanh:
case IntrinsicType::kTrunc:
case IntrinsicType::kSign:
return builder->Call(str.str(), "f2");
case IntrinsicType::kLdexp:
return builder->Call(str.str(), "f2", "i2");
case IntrinsicType::kAtan2:
case IntrinsicType::kDot:
case IntrinsicType::kDistance:
case IntrinsicType::kPow:
case IntrinsicType::kReflect:
case IntrinsicType::kStep:
return builder->Call(str.str(), "f2", "f2");
case IntrinsicType::kCross:
return builder->Call(str.str(), "f3", "f3");
case IntrinsicType::kFma:
case IntrinsicType::kMix:
case IntrinsicType::kFaceForward:
case IntrinsicType::kSmoothStep:
return builder->Call(str.str(), "f2", "f2", "f2");
case IntrinsicType::kAll:
case IntrinsicType::kAny:
return builder->Call(str.str(), "b2");
case IntrinsicType::kAbs:
if (type == ParamType::kF32) {
return builder->Call(str.str(), "f2");
} else {
return builder->Call(str.str(), "u2");
}
case IntrinsicType::kCountOneBits:
case IntrinsicType::kReverseBits:
return builder->Call(str.str(), "u2");
case IntrinsicType::kMax:
case IntrinsicType::kMin:
if (type == ParamType::kF32) {
return builder->Call(str.str(), "f2", "f2");
} else {
return builder->Call(str.str(), "u2", "u2");
}
case IntrinsicType::kClamp:
if (type == ParamType::kF32) {
return builder->Call(str.str(), "f2", "f2", "f2");
} else {
return builder->Call(str.str(), "u2", "u2", "u2");
}
case IntrinsicType::kSelect:
return builder->Call(str.str(), "f2", "f2", "b2");
case IntrinsicType::kDeterminant:
return builder->Call(str.str(), "m2x2");
case IntrinsicType::kTranspose:
return builder->Call(str.str(), "m3x2");
default:
break;
}
return nullptr;
}
using GlslIntrinsicTest = TestParamHelper<IntrinsicData>;
TEST_P(GlslIntrinsicTest, Emit) {
auto param = GetParam();
Global("f2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
Global("f3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
Global("u2", ty.vec2<u32>(), ast::StorageClass::kPrivate);
Global("i2", ty.vec2<i32>(), ast::StorageClass::kPrivate);
Global("b2", ty.vec2<bool>(), ast::StorageClass::kPrivate);
Global("m2x2", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
Global("m3x2", ty.mat3x2<f32>(), ast::StorageClass::kPrivate);
auto* call = GenerateCall(param.intrinsic, param.type, this);
ASSERT_NE(nullptr, call) << "Unhandled intrinsic";
Func("func", {}, ty.void_(), {CallStmt(call)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
GeneratorImpl& gen = Build();
auto* sem = program->Sem().Get(call);
ASSERT_NE(sem, nullptr);
auto* target = sem->Target();
ASSERT_NE(target, nullptr);
auto* intrinsic = target->As<sem::Intrinsic>();
ASSERT_NE(intrinsic, nullptr);
EXPECT_EQ(gen.generate_builtin_name(intrinsic), param.glsl_name);
}
INSTANTIATE_TEST_SUITE_P(
GlslGeneratorImplTest_Intrinsic,
GlslIntrinsicTest,
testing::Values(
IntrinsicData{IntrinsicType::kAbs, ParamType::kF32, "abs"},
IntrinsicData{IntrinsicType::kAbs, ParamType::kU32, "abs"},
IntrinsicData{IntrinsicType::kAcos, ParamType::kF32, "acos"},
IntrinsicData{IntrinsicType::kAll, ParamType::kBool, "all"},
IntrinsicData{IntrinsicType::kAny, ParamType::kBool, "any"},
IntrinsicData{IntrinsicType::kAsin, ParamType::kF32, "asin"},
IntrinsicData{IntrinsicType::kAtan, ParamType::kF32, "atan"},
IntrinsicData{IntrinsicType::kAtan2, ParamType::kF32, "atan"},
IntrinsicData{IntrinsicType::kCeil, ParamType::kF32, "ceil"},
IntrinsicData{IntrinsicType::kClamp, ParamType::kF32, "clamp"},
IntrinsicData{IntrinsicType::kClamp, ParamType::kU32, "clamp"},
IntrinsicData{IntrinsicType::kCos, ParamType::kF32, "cos"},
IntrinsicData{IntrinsicType::kCosh, ParamType::kF32, "cosh"},
IntrinsicData{IntrinsicType::kCountOneBits, ParamType::kU32,
"countbits"},
IntrinsicData{IntrinsicType::kCross, ParamType::kF32, "cross"},
IntrinsicData{IntrinsicType::kDeterminant, ParamType::kF32,
"determinant"},
IntrinsicData{IntrinsicType::kDistance, ParamType::kF32, "distance"},
IntrinsicData{IntrinsicType::kDot, ParamType::kF32, "dot"},
IntrinsicData{IntrinsicType::kDpdx, ParamType::kF32, "ddx"},
IntrinsicData{IntrinsicType::kDpdxCoarse, ParamType::kF32,
"ddx_coarse"},
IntrinsicData{IntrinsicType::kDpdxFine, ParamType::kF32, "ddx_fine"},
IntrinsicData{IntrinsicType::kDpdy, ParamType::kF32, "ddy"},
IntrinsicData{IntrinsicType::kDpdyCoarse, ParamType::kF32,
"ddy_coarse"},
IntrinsicData{IntrinsicType::kDpdyFine, ParamType::kF32, "ddy_fine"},
IntrinsicData{IntrinsicType::kExp, ParamType::kF32, "exp"},
IntrinsicData{IntrinsicType::kExp2, ParamType::kF32, "exp2"},
IntrinsicData{IntrinsicType::kFaceForward, ParamType::kF32,
"faceforward"},
IntrinsicData{IntrinsicType::kFloor, ParamType::kF32, "floor"},
IntrinsicData{IntrinsicType::kFma, ParamType::kF32, "mad"},
IntrinsicData{IntrinsicType::kFract, ParamType::kF32, "frac"},
IntrinsicData{IntrinsicType::kFwidth, ParamType::kF32, "fwidth"},
IntrinsicData{IntrinsicType::kFwidthCoarse, ParamType::kF32, "fwidth"},
IntrinsicData{IntrinsicType::kFwidthFine, ParamType::kF32, "fwidth"},
IntrinsicData{IntrinsicType::kInverseSqrt, ParamType::kF32, "rsqrt"},
IntrinsicData{IntrinsicType::kIsFinite, ParamType::kF32, "isfinite"},
IntrinsicData{IntrinsicType::kIsInf, ParamType::kF32, "isinf"},
IntrinsicData{IntrinsicType::kIsNan, ParamType::kF32, "isnan"},
IntrinsicData{IntrinsicType::kLdexp, ParamType::kF32, "ldexp"},
IntrinsicData{IntrinsicType::kLength, ParamType::kF32, "length"},
IntrinsicData{IntrinsicType::kLog, ParamType::kF32, "log"},
IntrinsicData{IntrinsicType::kLog2, ParamType::kF32, "log2"},
IntrinsicData{IntrinsicType::kMax, ParamType::kF32, "max"},
IntrinsicData{IntrinsicType::kMax, ParamType::kU32, "max"},
IntrinsicData{IntrinsicType::kMin, ParamType::kF32, "min"},
IntrinsicData{IntrinsicType::kMin, ParamType::kU32, "min"},
IntrinsicData{IntrinsicType::kMix, ParamType::kF32, "mix"},
IntrinsicData{IntrinsicType::kNormalize, ParamType::kF32, "normalize"},
IntrinsicData{IntrinsicType::kPow, ParamType::kF32, "pow"},
IntrinsicData{IntrinsicType::kReflect, ParamType::kF32, "reflect"},
IntrinsicData{IntrinsicType::kReverseBits, ParamType::kU32,
"reversebits"},
IntrinsicData{IntrinsicType::kRound, ParamType::kU32, "round"},
IntrinsicData{IntrinsicType::kSign, ParamType::kF32, "sign"},
IntrinsicData{IntrinsicType::kSin, ParamType::kF32, "sin"},
IntrinsicData{IntrinsicType::kSinh, ParamType::kF32, "sinh"},
IntrinsicData{IntrinsicType::kSmoothStep, ParamType::kF32,
"smoothstep"},
IntrinsicData{IntrinsicType::kSqrt, ParamType::kF32, "sqrt"},
IntrinsicData{IntrinsicType::kStep, ParamType::kF32, "step"},
IntrinsicData{IntrinsicType::kTan, ParamType::kF32, "tan"},
IntrinsicData{IntrinsicType::kTanh, ParamType::kF32, "tanh"},
IntrinsicData{IntrinsicType::kTranspose, ParamType::kF32, "transpose"},
IntrinsicData{IntrinsicType::kTrunc, ParamType::kF32, "trunc"}));
TEST_F(GlslGeneratorImplTest_Intrinsic, DISABLED_Intrinsic_IsNormal) {
FAIL();
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Intrinsic_Call) {
auto* call = Call("dot", "param1", "param2");
Global("param1", ty.vec3<f32>(), ast::StorageClass::kPrivate);
Global("param2", ty.vec3<f32>(), ast::StorageClass::kPrivate);
WrapInFunction(CallStmt(call));
GeneratorImpl& gen = Build();
gen.increment_indent();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
EXPECT_EQ(out.str(), "dot(param1, param2)");
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Select_Scalar) {
auto* call = Call("select", 1.0f, 2.0f, true);
WrapInFunction(CallStmt(call));
GeneratorImpl& gen = Build();
gen.increment_indent();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
EXPECT_EQ(out.str(), "(true ? 2.0f : 1.0f)");
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Select_Vector) {
auto* call =
Call("select", vec2<i32>(1, 2), vec2<i32>(3, 4), vec2<bool>(true, false));
WrapInFunction(CallStmt(call));
GeneratorImpl& gen = Build();
gen.increment_indent();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
EXPECT_EQ(out.str(), "(bvec2(true, false) ? ivec2(3, 4) : ivec2(1, 2))");
}
#if 0
TEST_F(GlslGeneratorImplTest_Intrinsic, Modf_Scalar) {
auto* res = Var("res", ty.f32());
auto* call = Call("modf", 1.0f, AddressOf(res));
WrapInFunction(res, call);
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr("modf(1.0f, res)"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Modf_Vector) {
auto* res = Var("res", ty.vec3<f32>());
auto* call = Call("modf", vec3<f32>(), AddressOf(res));
WrapInFunction(res, call);
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr("modf(vec3(0.0f, 0.0f, 0.0f), res)"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Frexp_Scalar_i32) {
auto* exp = Var("exp", ty.i32());
auto* call = Call("frexp", 1.0f, AddressOf(exp));
WrapInFunction(exp, call);
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr(R"(
float tint_tmp;
float tint_tmp_1 = frexp(1.0f, tint_tmp);
exp = int(tint_tmp);
tint_tmp_1;
)"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Frexp_Vector_i32) {
auto* res = Var("res", ty.vec3<i32>());
auto* call = Call("frexp", vec3<f32>(), AddressOf(res));
WrapInFunction(res, call);
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr(R"(
vec3 tint_tmp;
vec3 tint_tmp_1 = frexp(vec3(0.0f, 0.0f, 0.0f), tint_tmp);
res = ivec3(tint_tmp);
tint_tmp_1;
)"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, IsNormal_Scalar) {
auto* val = Var("val", ty.f32());
auto* call = Call("isNormal", val);
WrapInFunction(val, call);
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr(R"(
uint tint_isnormal_exponent = asuint(val) & 0x7f80000;
uint tint_isnormal_clamped = clamp(tint_isnormal_exponent, 0x0080000, 0x7f00000);
(tint_isnormal_clamped == tint_isnormal_exponent);
)"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, IsNormal_Vector) {
auto* val = Var("val", ty.vec3<f32>());
auto* call = Call("isNormal", val);
WrapInFunction(val, call);
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr(R"(
uvec3 tint_isnormal_exponent = asuint(val) & 0x7f80000;
uvec3 tint_isnormal_clamped = clamp(tint_isnormal_exponent, 0x0080000, 0x7f00000);
(tint_isnormal_clamped == tint_isnormal_exponent);
)"));
}
#endif
TEST_F(GlslGeneratorImplTest_Intrinsic, Degrees_Scalar) {
auto* val = Var("val", ty.f32());
auto* call = Call("degrees", val);
WrapInFunction(val, call);
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(#version 310 es
precision mediump float;
float tint_degrees(float param_0) {
return param_0 * 57.295779513082322865;
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void test_function() {
float val = 0.0f;
float tint_symbol = tint_degrees(val);
return;
}
void main() {
test_function();
}
)");
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Degrees_Vector) {
auto* val = Var("val", ty.vec3<f32>());
auto* call = Call("degrees", val);
WrapInFunction(val, call);
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(#version 310 es
precision mediump float;
vec3 tint_degrees(vec3 param_0) {
return param_0 * 57.295779513082322865;
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void test_function() {
vec3 val = vec3(0.0f, 0.0f, 0.0f);
vec3 tint_symbol = tint_degrees(val);
return;
}
void main() {
test_function();
}
)");
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Radians_Scalar) {
auto* val = Var("val", ty.f32());
auto* call = Call("radians", val);
WrapInFunction(val, call);
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(#version 310 es
precision mediump float;
float tint_radians(float param_0) {
return param_0 * 0.017453292519943295474;
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void test_function() {
float val = 0.0f;
float tint_symbol = tint_radians(val);
return;
}
void main() {
test_function();
}
)");
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Radians_Vector) {
auto* val = Var("val", ty.vec3<f32>());
auto* call = Call("radians", val);
WrapInFunction(val, call);
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(#version 310 es
precision mediump float;
vec3 tint_radians(vec3 param_0) {
return param_0 * 0.017453292519943295474;
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void test_function() {
vec3 val = vec3(0.0f, 0.0f, 0.0f);
vec3 tint_symbol = tint_radians(val);
return;
}
void main() {
test_function();
}
)");
}
#if 0
TEST_F(GlslGeneratorImplTest_Intrinsic, Pack4x8Snorm) {
auto* call = Call("pack4x8snorm", "p1");
Global("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
WrapInFunction(CallStmt(call));
GeneratorImpl& gen = Build();
gen.increment_indent();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr("ivec4 tint_tmp = ivec4(round(clamp(p1, "
"-1.0, 1.0) * 127.0)) & 0xff;"));
EXPECT_THAT(out.str(), HasSubstr("asuint(tint_tmp.x | tint_tmp.y << 8 | "
"tint_tmp.z << 16 | tint_tmp.w << 24)"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Pack4x8Unorm) {
auto* call = Call("pack4x8unorm", "p1");
Global("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
WrapInFunction(CallStmt(call));
GeneratorImpl& gen = Build();
gen.increment_indent();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr("uvec4 tint_tmp = uvec4(round(clamp(p1, "
"0.0, 1.0) * 255.0));"));
EXPECT_THAT(out.str(), HasSubstr("(tint_tmp.x | tint_tmp.y << 8 | "
"tint_tmp.z << 16 | tint_tmp.w << 24)"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Pack2x16Snorm) {
auto* call = Call("pack2x16snorm", "p1");
Global("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
WrapInFunction(CallStmt(call));
GeneratorImpl& gen = Build();
gen.increment_indent();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr("int2 tint_tmp = int2(round(clamp(p1, "
"-1.0, 1.0) * 32767.0)) & 0xffff;"));
EXPECT_THAT(out.str(), HasSubstr("asuint(tint_tmp.x | tint_tmp.y << 16)"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Pack2x16Unorm) {
auto* call = Call("pack2x16unorm", "p1");
Global("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
WrapInFunction(CallStmt(call));
GeneratorImpl& gen = Build();
gen.increment_indent();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr("uint2 tint_tmp = uint2(round(clamp(p1, "
"0.0, 1.0) * 65535.0));"));
EXPECT_THAT(out.str(), HasSubstr("(tint_tmp.x | tint_tmp.y << 16)"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Pack2x16Float) {
auto* call = Call("pack2x16float", "p1");
Global("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
WrapInFunction(CallStmt(call));
GeneratorImpl& gen = Build();
gen.increment_indent();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr("uint2 tint_tmp = f32tof16(p1);"));
EXPECT_THAT(out.str(), HasSubstr("(tint_tmp.x | tint_tmp.y << 16)"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Unpack4x8Snorm) {
auto* call = Call("unpack4x8snorm", "p1");
Global("p1", ty.u32(), ast::StorageClass::kPrivate);
WrapInFunction(CallStmt(call));
GeneratorImpl& gen = Build();
gen.increment_indent();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr("int tint_tmp_1 = int(p1);"));
EXPECT_THAT(gen.result(),
HasSubstr("ivec4 tint_tmp = ivec4(tint_tmp_1 << 24, tint_tmp_1 "
"<< 16, tint_tmp_1 << 8, tint_tmp_1) >> 24;"));
EXPECT_THAT(out.str(),
HasSubstr("clamp(float4(tint_tmp) / 127.0, -1.0, 1.0)"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Unpack4x8Unorm) {
auto* call = Call("unpack4x8unorm", "p1");
Global("p1", ty.u32(), ast::StorageClass::kPrivate);
WrapInFunction(CallStmt(call));
GeneratorImpl& gen = Build();
gen.increment_indent();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr("uint tint_tmp_1 = p1;"));
EXPECT_THAT(
gen.result(),
HasSubstr("uvec4 tint_tmp = uvec4(tint_tmp_1 & 0xff, (tint_tmp_1 >> "
"8) & 0xff, (tint_tmp_1 >> 16) & 0xff, tint_tmp_1 >> 24);"));
EXPECT_THAT(out.str(), HasSubstr("float4(tint_tmp) / 255.0"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Unpack2x16Snorm) {
auto* call = Call("unpack2x16snorm", "p1");
Global("p1", ty.u32(), ast::StorageClass::kPrivate);
WrapInFunction(CallStmt(call));
GeneratorImpl& gen = Build();
gen.increment_indent();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr("int tint_tmp_1 = int(p1);"));
EXPECT_THAT(
gen.result(),
HasSubstr("int2 tint_tmp = int2(tint_tmp_1 << 16, tint_tmp_1) >> 16;"));
EXPECT_THAT(out.str(),
HasSubstr("clamp(float2(tint_tmp) / 32767.0, -1.0, 1.0)"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Unpack2x16Unorm) {
auto* call = Call("unpack2x16unorm", "p1");
Global("p1", ty.u32(), ast::StorageClass::kPrivate);
WrapInFunction(CallStmt(call));
GeneratorImpl& gen = Build();
gen.increment_indent();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr("uint tint_tmp_1 = p1;"));
EXPECT_THAT(gen.result(),
HasSubstr("uint2 tint_tmp = uint2(tint_tmp_1 & 0xffff, "
"tint_tmp_1 >> 16);"));
EXPECT_THAT(out.str(), HasSubstr("float2(tint_tmp) / 65535.0"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Unpack2x16Float) {
auto* call = Call("unpack2x16float", "p1");
Global("p1", ty.u32(), ast::StorageClass::kPrivate);
WrapInFunction(CallStmt(call));
GeneratorImpl& gen = Build();
gen.increment_indent();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
EXPECT_THAT(gen.result(), HasSubstr("uint tint_tmp = p1;"));
EXPECT_THAT(out.str(),
HasSubstr("f16tof32(uint2(tint_tmp & 0xffff, tint_tmp >> 16))"));
}
TEST_F(GlslGeneratorImplTest_Intrinsic, StorageBarrier) {
Func("main", {}, ty.void_(),
{CallStmt(Call("storageBarrier"))},
{
Stage(ast::PipelineStage::kCompute),
WorkgroupSize(1),
});
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void main() {
DeviceMemoryBarrierWithGroupSync();
return;
}
)");
}
TEST_F(GlslGeneratorImplTest_Intrinsic, WorkgroupBarrier) {
Func("main", {}, ty.void_(),
{CallStmt(Call("workgroupBarrier"))},
{
Stage(ast::PipelineStage::kCompute),
WorkgroupSize(1),
});
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void main() {
GroupMemoryBarrierWithGroupSync();
return;
}
)");
}
TEST_F(GlslGeneratorImplTest_Intrinsic, Ignore) {
Func("f", {Param("a", ty.i32()), Param("b", ty.i32()), Param("c", ty.i32())},
ty.i32(), {Return(Mul(Add("a", "b"), "c"))});
Func("main", {}, ty.void_(),
{CallStmt(Call("ignore", Call("f", 1, 2, 3)))},
{
Stage(ast::PipelineStage::kCompute),
WorkgroupSize(1),
});
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(int f(int a, int b, int c) {
return ((a + b) * c);
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void main() {
f(1, 2, 3);
return;
}
)");
}
#endif
TEST_F(GlslGeneratorImplTest_Intrinsic, DotI32) {
Global("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
WrapInFunction(CallStmt(Call("dot", "v", "v")));
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(#version 310 es
precision mediump float;
int tint_int_dot(ivec3 a, ivec3 b) {
return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
}
ivec3 v = ivec3(0, 0, 0);
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void test_function() {
tint_int_dot(v, v);
return;
}
void main() {
test_function();
}
)");
}
TEST_F(GlslGeneratorImplTest_Intrinsic, DotU32) {
Global("v", ty.vec3<u32>(), ast::StorageClass::kPrivate);
WrapInFunction(CallStmt(Call("dot", "v", "v")));
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(#version 310 es
precision mediump float;
uint tint_int_dot(uvec3 a, uvec3 b) {
return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
}
uvec3 v = uvec3(0u, 0u, 0u);
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void test_function() {
tint_int_dot(v, v);
return;
}
void main() {
test_function();
}
)");
}
} // namespace
} // namespace glsl
} // namespace writer
} // namespace tint