Implement data unpacking intrinsics
* Add support for data unpacking intrinsics
* spir-v reader
* type determiner
* intrinsic table
* spir-v, hlsl and msl writers
Bug: tint:341
Change-Id: I8f40d19d59a4699af75cd579fe8398c735a77a59
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/41320
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: Alan Baker <alanbaker@google.com>
diff --git a/src/intrinsic_table.cc b/src/intrinsic_table.cc
index 0c99427..a0f3cc2 100644
--- a/src/intrinsic_table.cc
+++ b/src/intrinsic_table.cc
@@ -886,127 +886,132 @@
// clang-format off
- // name return type parameter types open type constraints // NOLINT
- Register(I::kAbs, T, {T}, {OpenType::T, fiu32} ); // NOLINT
- Register(I::kAbs, vecN_T, {vecN_T}, {OpenType::T, fiu32} ); // NOLINT
- Register(I::kAcos, f32, {f32} ); // NOLINT
- Register(I::kAcos, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kAll, bool_, {vecN_bool} ); // NOLINT
- Register(I::kAny, bool_, {vecN_bool} ); // NOLINT
- Register(I::kArrayLength, u32, {array_T} ); // NOLINT
- Register(I::kAsin, f32, {f32} ); // NOLINT
- Register(I::kAsin, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kAtan, f32, {f32} ); // NOLINT
- Register(I::kAtan, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kAtan2, f32, {f32, f32} ); // NOLINT
- Register(I::kAtan2, vecN_f32, {vecN_f32, vecN_f32} ); // NOLINT
- Register(I::kCeil, f32, {f32} ); // NOLINT
- Register(I::kCeil, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kClamp, T, {T, T, T}, {OpenType::T, fiu32} ); // NOLINT
- Register(I::kClamp, vecN_T, {vecN_T, vecN_T, vecN_T}, {OpenType::T, fiu32} ); // NOLINT
- Register(I::kCos, f32, {f32} ); // NOLINT
- Register(I::kCos, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kCosh, f32, {f32} ); // NOLINT
- Register(I::kCosh, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kCountOneBits, T, {T}, {OpenType::T, iu32} ); // NOLINT
- Register(I::kCountOneBits, vecN_T, {vecN_T}, {OpenType::T, iu32} ); // NOLINT
- Register(I::kCross, vec3_f32, {vec3_f32, vec3_f32} ); // NOLINT
- Register(I::kDeterminant, f32, {matNxN_f32} ); // NOLINT
- Register(I::kDistance, f32, {f32, f32} ); // NOLINT
- Register(I::kDistance, f32, {vecN_f32, vecN_f32} ); // NOLINT
- Register(I::kDot, f32, {vecN_f32, vecN_f32} ); // NOLINT
- Register(I::kDpdx, f32, {f32} ); // NOLINT
- Register(I::kDpdx, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kDpdxCoarse, f32, {f32} ); // NOLINT
- Register(I::kDpdxCoarse, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kDpdxFine, f32, {f32} ); // NOLINT
- Register(I::kDpdxFine, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kDpdy, f32, {f32} ); // NOLINT
- Register(I::kDpdy, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kDpdyCoarse, f32, {f32} ); // NOLINT
- Register(I::kDpdyCoarse, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kDpdyFine, f32, {f32} ); // NOLINT
- Register(I::kDpdyFine, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kExp, f32, {f32} ); // NOLINT
- Register(I::kExp, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kExp2, f32, {f32} ); // NOLINT
- Register(I::kExp2, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kFaceForward, f32, {f32, f32, f32} ); // NOLINT
- Register(I::kFaceForward, vecN_f32, {vecN_f32, vecN_f32, vecN_f32} ); // NOLINT
- Register(I::kFloor, f32, {f32} ); // NOLINT
- Register(I::kFloor, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kFma, f32, {f32, f32, f32} ); // NOLINT
- Register(I::kFma, vecN_f32, {vecN_f32, vecN_f32, vecN_f32} ); // NOLINT
- Register(I::kFract, f32, {f32} ); // NOLINT
- Register(I::kFract, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kFrexp, f32, {f32, ptr_T}, {OpenType::T, iu32} ); // NOLINT
- Register(I::kFrexp, vecN_f32, {vecN_f32, ptr_vecN_T}, {OpenType::T, iu32} ); // NOLINT
- Register(I::kFwidth, f32, {f32} ); // NOLINT
- Register(I::kFwidth, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kFwidthCoarse, f32, {f32} ); // NOLINT
- Register(I::kFwidthCoarse, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kFwidthFine, f32, {f32} ); // NOLINT
- Register(I::kFwidthFine, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kInverseSqrt, f32, {f32} ); // NOLINT
- Register(I::kInverseSqrt, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kIsFinite, bool_, {f32} ); // NOLINT
- Register(I::kIsFinite, vecN_bool, {vecN_f32} ); // NOLINT
- Register(I::kIsInf, bool_, {f32} ); // NOLINT
- Register(I::kIsInf, vecN_bool, {vecN_f32} ); // NOLINT
- Register(I::kIsNan, bool_, {f32} ); // NOLINT
- Register(I::kIsNan, vecN_bool, {vecN_f32} ); // NOLINT
- Register(I::kIsNormal, bool_, {f32} ); // NOLINT
- Register(I::kIsNormal, vecN_bool, {vecN_f32} ); // NOLINT
- Register(I::kLdexp, f32, {f32, T}, {OpenType::T, iu32} ); // NOLINT
- Register(I::kLdexp, vecN_f32, {vecN_f32, vecN_T}, {OpenType::T, iu32} ); // NOLINT
- Register(I::kLength, f32, {f32} ); // NOLINT
- Register(I::kLength, f32, {vecN_f32} ); // NOLINT
- Register(I::kLog, f32, {f32} ); // NOLINT
- Register(I::kLog, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kLog2, f32, {f32} ); // NOLINT
- Register(I::kLog2, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kMax, T, {T, T}, {OpenType::T, fiu32} ); // NOLINT
- Register(I::kMax, vecN_T, {vecN_T, vecN_T}, {OpenType::T, fiu32} ); // NOLINT
- Register(I::kMin, T, {T, T}, {OpenType::T, fiu32} ); // NOLINT
- Register(I::kMin, vecN_T, {vecN_T, vecN_T}, {OpenType::T, fiu32} ); // NOLINT
- Register(I::kMix, f32, {f32, f32, f32} ); // NOLINT
- Register(I::kMix, vecN_f32, {vecN_f32, vecN_f32, vecN_f32} ); // NOLINT
- Register(I::kModf, f32, {f32, ptr_f32} ); // NOLINT
- Register(I::kModf, vecN_f32, {vecN_f32, ptr_vecN_f32} ); // NOLINT
- Register(I::kNormalize, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kPack2x16Float, u32, {vec2_f32} ); // NOLINT
- Register(I::kPack2x16Snorm, u32, {vec2_f32} ); // NOLINT
- Register(I::kPack2x16Unorm, u32, {vec2_f32} ); // NOLINT
- Register(I::kPack4x8Snorm, u32, {vec4_f32} ); // NOLINT
- Register(I::kPack4x8Unorm, u32, {vec4_f32} ); // NOLINT
- Register(I::kPow, f32, {f32, f32} ); // NOLINT
- Register(I::kPow, vecN_f32, {vecN_f32, vecN_f32} ); // NOLINT
- Register(I::kReflect, f32, {f32, f32} ); // NOLINT
- Register(I::kReflect, vecN_f32, {vecN_f32, vecN_f32} ); // NOLINT
- Register(I::kReverseBits, T, {T}, {OpenType::T, iu32} ); // NOLINT
- Register(I::kReverseBits, vecN_T, {vecN_T}, {OpenType::T, iu32} ); // NOLINT
- Register(I::kRound, f32, {f32} ); // NOLINT
- Register(I::kRound, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kSelect, T, {T, T, bool_}, {OpenType::T, scalar} ); // NOLINT
- Register(I::kSelect, vecN_T, {vecN_T, vecN_T, vecN_bool}, {OpenType::T, scalar} ); // NOLINT
- Register(I::kSign, f32, {f32} ); // NOLINT
- Register(I::kSign, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kSin, f32, {f32} ); // NOLINT
- Register(I::kSin, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kSinh, f32, {f32} ); // NOLINT
- Register(I::kSinh, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kSmoothStep, f32, {f32, f32, f32} ); // NOLINT
- Register(I::kSmoothStep, vecN_f32, {vecN_f32, vecN_f32, vecN_f32} ); // NOLINT
- Register(I::kSqrt, f32, {f32} ); // NOLINT
- Register(I::kSqrt, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kStep, f32, {f32, f32} ); // NOLINT
- Register(I::kStep, vecN_f32, {vecN_f32, vecN_f32} ); // NOLINT
- Register(I::kTan, f32, {f32} ); // NOLINT
- Register(I::kTan, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kTanh, f32, {f32} ); // NOLINT
- Register(I::kTanh, vecN_f32, {vecN_f32} ); // NOLINT
- Register(I::kTrunc, f32, {f32} ); // NOLINT
- Register(I::kTrunc, vecN_f32, {vecN_f32} ); // NOLINT
+ // name return type parameter types open type constraints // NOLINT
+ Register(I::kAbs, T, {T}, {OpenType::T, fiu32} ); // NOLINT
+ Register(I::kAbs, vecN_T, {vecN_T}, {OpenType::T, fiu32} ); // NOLINT
+ Register(I::kAcos, f32, {f32} ); // NOLINT
+ Register(I::kAcos, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kAll, bool_, {vecN_bool} ); // NOLINT
+ Register(I::kAny, bool_, {vecN_bool} ); // NOLINT
+ Register(I::kArrayLength, u32, {array_T} ); // NOLINT
+ Register(I::kAsin, f32, {f32} ); // NOLINT
+ Register(I::kAsin, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kAtan, f32, {f32} ); // NOLINT
+ Register(I::kAtan, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kAtan2, f32, {f32, f32} ); // NOLINT
+ Register(I::kAtan2, vecN_f32, {vecN_f32, vecN_f32} ); // NOLINT
+ Register(I::kCeil, f32, {f32} ); // NOLINT
+ Register(I::kCeil, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kClamp, T, {T, T, T}, {OpenType::T, fiu32} ); // NOLINT
+ Register(I::kClamp, vecN_T, {vecN_T, vecN_T, vecN_T}, {OpenType::T, fiu32} ); // NOLINT
+ Register(I::kCos, f32, {f32} ); // NOLINT
+ Register(I::kCos, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kCosh, f32, {f32} ); // NOLINT
+ Register(I::kCosh, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kCountOneBits, T, {T}, {OpenType::T, iu32} ); // NOLINT
+ Register(I::kCountOneBits, vecN_T, {vecN_T}, {OpenType::T, iu32} ); // NOLINT
+ Register(I::kCross, vec3_f32, {vec3_f32, vec3_f32} ); // NOLINT
+ Register(I::kDeterminant, f32, {matNxN_f32} ); // NOLINT
+ Register(I::kDistance, f32, {f32, f32} ); // NOLINT
+ Register(I::kDistance, f32, {vecN_f32, vecN_f32} ); // NOLINT
+ Register(I::kDot, f32, {vecN_f32, vecN_f32} ); // NOLINT
+ Register(I::kDpdx, f32, {f32} ); // NOLINT
+ Register(I::kDpdx, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kDpdxCoarse, f32, {f32} ); // NOLINT
+ Register(I::kDpdxCoarse, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kDpdxFine, f32, {f32} ); // NOLINT
+ Register(I::kDpdxFine, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kDpdy, f32, {f32} ); // NOLINT
+ Register(I::kDpdy, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kDpdyCoarse, f32, {f32} ); // NOLINT
+ Register(I::kDpdyCoarse, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kDpdyFine, f32, {f32} ); // NOLINT
+ Register(I::kDpdyFine, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kExp, f32, {f32} ); // NOLINT
+ Register(I::kExp, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kExp2, f32, {f32} ); // NOLINT
+ Register(I::kExp2, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kFaceForward, f32, {f32, f32, f32} ); // NOLINT
+ Register(I::kFaceForward, vecN_f32, {vecN_f32, vecN_f32, vecN_f32} ); // NOLINT
+ Register(I::kFloor, f32, {f32} ); // NOLINT
+ Register(I::kFloor, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kFma, f32, {f32, f32, f32} ); // NOLINT
+ Register(I::kFma, vecN_f32, {vecN_f32, vecN_f32, vecN_f32} ); // NOLINT
+ Register(I::kFract, f32, {f32} ); // NOLINT
+ Register(I::kFract, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kFrexp, f32, {f32, ptr_T}, {OpenType::T, iu32} ); // NOLINT
+ Register(I::kFrexp, vecN_f32, {vecN_f32, ptr_vecN_T}, {OpenType::T, iu32} ); // NOLINT
+ Register(I::kFwidth, f32, {f32} ); // NOLINT
+ Register(I::kFwidth, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kFwidthCoarse, f32, {f32} ); // NOLINT
+ Register(I::kFwidthCoarse, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kFwidthFine, f32, {f32} ); // NOLINT
+ Register(I::kFwidthFine, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kInverseSqrt, f32, {f32} ); // NOLINT
+ Register(I::kInverseSqrt, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kIsFinite, bool_, {f32} ); // NOLINT
+ Register(I::kIsFinite, vecN_bool, {vecN_f32} ); // NOLINT
+ Register(I::kIsInf, bool_, {f32} ); // NOLINT
+ Register(I::kIsInf, vecN_bool, {vecN_f32} ); // NOLINT
+ Register(I::kIsNan, bool_, {f32} ); // NOLINT
+ Register(I::kIsNan, vecN_bool, {vecN_f32} ); // NOLINT
+ Register(I::kIsNormal, bool_, {f32} ); // NOLINT
+ Register(I::kIsNormal, vecN_bool, {vecN_f32} ); // NOLINT
+ Register(I::kLdexp, f32, {f32, T}, {OpenType::T, iu32} ); // NOLINT
+ Register(I::kLdexp, vecN_f32, {vecN_f32, vecN_T}, {OpenType::T, iu32} ); // NOLINT
+ Register(I::kLength, f32, {f32} ); // NOLINT
+ Register(I::kLength, f32, {vecN_f32} ); // NOLINT
+ Register(I::kLog, f32, {f32} ); // NOLINT
+ Register(I::kLog, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kLog2, f32, {f32} ); // NOLINT
+ Register(I::kLog2, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kMax, T, {T, T}, {OpenType::T, fiu32} ); // NOLINT
+ Register(I::kMax, vecN_T, {vecN_T, vecN_T}, {OpenType::T, fiu32} ); // NOLINT
+ Register(I::kMin, T, {T, T}, {OpenType::T, fiu32} ); // NOLINT
+ Register(I::kMin, vecN_T, {vecN_T, vecN_T}, {OpenType::T, fiu32} ); // NOLINT
+ Register(I::kMix, f32, {f32, f32, f32} ); // NOLINT
+ Register(I::kMix, vecN_f32, {vecN_f32, vecN_f32, vecN_f32} ); // NOLINT
+ Register(I::kModf, f32, {f32, ptr_f32} ); // NOLINT
+ Register(I::kModf, vecN_f32, {vecN_f32, ptr_vecN_f32} ); // NOLINT
+ Register(I::kNormalize, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kPack2x16Float, u32, {vec2_f32} ); // NOLINT
+ Register(I::kPack2x16Snorm, u32, {vec2_f32} ); // NOLINT
+ Register(I::kPack2x16Unorm, u32, {vec2_f32} ); // NOLINT
+ Register(I::kPack4x8Snorm, u32, {vec4_f32} ); // NOLINT
+ Register(I::kPack4x8Unorm, u32, {vec4_f32} ); // NOLINT
+ Register(I::kPow, f32, {f32, f32} ); // NOLINT
+ Register(I::kPow, vecN_f32, {vecN_f32, vecN_f32} ); // NOLINT
+ Register(I::kReflect, f32, {f32, f32} ); // NOLINT
+ Register(I::kReflect, vecN_f32, {vecN_f32, vecN_f32} ); // NOLINT
+ Register(I::kReverseBits, T, {T}, {OpenType::T, iu32} ); // NOLINT
+ Register(I::kReverseBits, vecN_T, {vecN_T}, {OpenType::T, iu32} ); // NOLINT
+ Register(I::kRound, f32, {f32} ); // NOLINT
+ Register(I::kRound, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kSelect, T, {T, T, bool_}, {OpenType::T, scalar} ); // NOLINT
+ Register(I::kSelect, vecN_T, {vecN_T, vecN_T, vecN_bool}, {OpenType::T, scalar} ); // NOLINT
+ Register(I::kSign, f32, {f32} ); // NOLINT
+ Register(I::kSign, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kSin, f32, {f32} ); // NOLINT
+ Register(I::kSin, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kSinh, f32, {f32} ); // NOLINT
+ Register(I::kSinh, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kSmoothStep, f32, {f32, f32, f32} ); // NOLINT
+ Register(I::kSmoothStep, vecN_f32, {vecN_f32, vecN_f32, vecN_f32} ); // NOLINT
+ Register(I::kSqrt, f32, {f32} ); // NOLINT
+ Register(I::kSqrt, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kStep, f32, {f32, f32} ); // NOLINT
+ Register(I::kStep, vecN_f32, {vecN_f32, vecN_f32} ); // NOLINT
+ Register(I::kTan, f32, {f32} ); // NOLINT
+ Register(I::kTan, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kTanh, f32, {f32} ); // NOLINT
+ Register(I::kTanh, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kTrunc, f32, {f32} ); // NOLINT
+ Register(I::kTrunc, vecN_f32, {vecN_f32} ); // NOLINT
+ Register(I::kUnpack2x16Float, vec2_f32, {u32} ); // NOLINT
+ Register(I::kUnpack2x16Snorm, vec2_f32, {u32} ); // NOLINT
+ Register(I::kUnpack2x16Unorm, vec2_f32, {u32} ); // NOLINT
+ Register(I::kUnpack4x8Snorm, vec4_f32, {u32} ); // NOLINT
+ Register(I::kUnpack4x8Unorm, vec4_f32, {u32} ); // NOLINT
// clang-format on
auto* tex_1d_f32 = sampled_texture(Dim::k1d, f32);
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 46fb806..b68b9c9 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -425,6 +425,16 @@
return "tanh";
case GLSLstd450Trunc:
return "trunc";
+ case GLSLstd450UnpackSnorm4x8:
+ return "unpack4x8snorm";
+ case GLSLstd450UnpackUnorm4x8:
+ return "unpack4x8unorm";
+ case GLSLstd450UnpackSnorm2x16:
+ return "unpack2x16snorm";
+ case GLSLstd450UnpackUnorm2x16:
+ return "unpack2x16unorm";
+ case GLSLstd450UnpackHalf2x16:
+ return "unpack2x16float";
default:
// TODO(dneto) - The following are not implemented.
@@ -448,11 +458,6 @@
case GLSLstd450FrexpStruct:
case GLSLstd450PackDouble2x32:
- case GLSLstd450UnpackSnorm2x16:
- case GLSLstd450UnpackUnorm2x16:
- case GLSLstd450UnpackHalf2x16:
- case GLSLstd450UnpackSnorm4x8:
- case GLSLstd450UnpackUnorm4x8:
case GLSLstd450UnpackDouble2x32:
case GLSLstd450Refract:
diff --git a/src/reader/spirv/function_glsl_std_450_test.cc b/src/reader/spirv/function_glsl_std_450_test.cc
index b876e9a..d0c43f1 100644
--- a/src/reader/spirv/function_glsl_std_450_test.cc
+++ b/src/reader/spirv/function_glsl_std_450_test.cc
@@ -1548,6 +1548,50 @@
{"PackUnorm2x16", "pack2x16unorm", 2},
{"PackHalf2x16", "pack2x16float", 2}}));
+using SpvParserTest_GlslStd450_DataUnpacking =
+ SpvParserTestBase<::testing::TestWithParam<DataPackingCase>>;
+
+TEST_P(SpvParserTest_GlslStd450_DataUnpacking, Valid) {
+ auto param = GetParam();
+ const auto assembly = Preamble() + R"(
+ %1 = OpExtInst )" + (param.vec_size == 2 ? "%v2float" : "%v4float") +
+ std::string(" %glsl ") + param.opcode + R"( %u1
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+ FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ const auto body = ToString(p->builder(), fe.ast_body());
+ EXPECT_THAT(body, HasSubstr(R"(
+ VariableConst{
+ x_1
+ none
+ )" + std::string(param.vec_size == 2 ? "__vec_2__f32" : "__vec_4__f32") +
+ R"(
+ {
+ Call[not set]{
+ Identifier[not set]{)" +
+ param.wgsl_func + R"(}
+ (
+ Identifier[not set]{u1}
+ )
+ }
+ }
+ })"))
+ << body;
+}
+
+INSTANTIATE_TEST_SUITE_P(Samples,
+ SpvParserTest_GlslStd450_DataUnpacking,
+ ::testing::ValuesIn(std::vector<DataPackingCase>{
+ {"UnpackSnorm4x8", "unpack4x8snorm", 4},
+ {"UnpackUnorm4x8", "unpack4x8unorm", 4},
+ {"UnpackSnorm2x16", "unpack2x16snorm", 2},
+ {"UnpackUnorm2x16", "unpack2x16unorm", 2},
+ {"UnpackHalf2x16", "unpack2x16float", 2}}));
+
} // namespace
} // namespace spirv
} // namespace reader
diff --git a/src/semantic/intrinsic.h b/src/semantic/intrinsic.h
index 6cc116b..d8a313b 100644
--- a/src/semantic/intrinsic.h
+++ b/src/semantic/intrinsic.h
@@ -101,7 +101,12 @@
kTextureSampleGrad,
kTextureSampleLevel,
kTextureStore,
- kTrunc
+ kTrunc,
+ kUnpack4x8Snorm,
+ kUnpack4x8Unorm,
+ kUnpack2x16Snorm,
+ kUnpack2x16Unorm,
+ kUnpack2x16Float,
};
/// @returns the name of the intrinsic function type. The spelling, including
@@ -143,6 +148,11 @@
/// @returns true if the given `i` is a data packing intrinsic
bool IsDataPackingIntrinsic(IntrinsicType i);
+/// Determines if the given `i` is a data unpacking intrinsic
+/// @param i the intrinsic
+/// @returns true if the given `i` is a data unpacking intrinsic
+bool IsDataUnpackingIntrinsic(IntrinsicType i);
+
/// Intrinsic holds the semantic information for an intrinsic function.
class Intrinsic : public Castable<Intrinsic, CallTarget> {
public:
@@ -185,6 +195,9 @@
/// @returns true if intrinsic is a data packing intrinsic
bool IsDataPacking() const;
+ /// @returns true if intrinsic is a data unpacking intrinsic
+ bool IsDataUnpacking() const;
+
private:
IntrinsicType const type_;
};
diff --git a/src/semantic/sem_intrinsic.cc b/src/semantic/sem_intrinsic.cc
index aa398c0..9e610ee 100644
--- a/src/semantic/sem_intrinsic.cc
+++ b/src/semantic/sem_intrinsic.cc
@@ -188,6 +188,16 @@
return "textureStore";
case IntrinsicType::kTrunc:
return "trunc";
+ case IntrinsicType::kUnpack4x8Snorm:
+ return "unpack4x8snorm";
+ case IntrinsicType::kUnpack4x8Unorm:
+ return "unpack4x8unorm";
+ case IntrinsicType::kUnpack2x16Snorm:
+ return "unpack2x16snorm";
+ case IntrinsicType::kUnpack2x16Unorm:
+ return "unpack2x16unorm";
+ case IntrinsicType::kUnpack2x16Float:
+ return "unpack2x16float";
}
return "<unknown>";
}
@@ -238,6 +248,14 @@
i == IntrinsicType::kPack2x16Float;
}
+bool IsDataUnpackingIntrinsic(IntrinsicType i) {
+ return i == IntrinsicType::kUnpack4x8Snorm ||
+ i == IntrinsicType::kUnpack4x8Unorm ||
+ i == IntrinsicType::kUnpack2x16Snorm ||
+ i == IntrinsicType::kUnpack2x16Unorm ||
+ i == IntrinsicType::kUnpack2x16Float;
+}
+
Intrinsic::Intrinsic(IntrinsicType type,
type::Type* return_type,
const ParameterList& parameters)
@@ -273,5 +291,9 @@
return IsDataPackingIntrinsic(type_);
}
+bool Intrinsic::IsDataUnpacking() const {
+ return IsDataUnpackingIntrinsic(type_);
+}
+
} // namespace semantic
} // namespace tint
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index 18f52f6..a127caa 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -717,6 +717,16 @@
return IntrinsicType::kTextureSampleLevel;
} else if (name == "trunc") {
return IntrinsicType::kTrunc;
+ } else if (name == "unpack4x8snorm") {
+ return IntrinsicType::kUnpack4x8Snorm;
+ } else if (name == "unpack4x8unorm") {
+ return IntrinsicType::kUnpack4x8Unorm;
+ } else if (name == "unpack2x16snorm") {
+ return IntrinsicType::kUnpack2x16Snorm;
+ } else if (name == "unpack2x16unorm") {
+ return IntrinsicType::kUnpack2x16Unorm;
+ } else if (name == "unpack2x16float") {
+ return IntrinsicType::kUnpack2x16Float;
}
return IntrinsicType::kNone;
}
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index 71e0371..fc46468 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -1794,6 +1794,36 @@
IntrinsicData{"pack2x16unorm", IntrinsicType::kPack2x16Unorm},
IntrinsicData{"pack2x16float", IntrinsicType::kPack2x16Float}));
+using ImportData_DataUnpackingTest = TypeDeterminerTestWithParam<IntrinsicData>;
+TEST_P(ImportData_DataUnpackingTest, InferType) {
+ auto param = GetParam();
+
+ bool pack4 = param.intrinsic == IntrinsicType::kUnpack4x8Snorm ||
+ param.intrinsic == IntrinsicType::kUnpack4x8Unorm;
+
+ auto* call = Call(param.name, 1u);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(td()->Determine()) << td()->error();
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ if (pack4) {
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 4u);
+ } else {
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 2u);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ TypeDeterminerTest,
+ ImportData_DataUnpackingTest,
+ testing::Values(
+ IntrinsicData{"unpack4x8snorm", IntrinsicType::kUnpack4x8Snorm},
+ IntrinsicData{"unpack4x8unorm", IntrinsicType::kUnpack4x8Unorm},
+ IntrinsicData{"unpack2x16snorm", IntrinsicType::kUnpack2x16Snorm},
+ IntrinsicData{"unpack2x16unorm", IntrinsicType::kUnpack2x16Unorm},
+ IntrinsicData{"unpack2x16float", IntrinsicType::kUnpack2x16Float}));
+
using ImportData_SingleParamTest = TypeDeterminerTestWithParam<IntrinsicData>;
TEST_P(ImportData_SingleParamTest, Scalar) {
auto param = GetParam();
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index d370a62..b170fbd 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -556,6 +556,8 @@
return false;
} else if (intrinsic->IsDataPacking()) {
return EmitDataPackingCall(pre, out, expr, intrinsic);
+ } else if (intrinsic->IsDataUnpacking()) {
+ return EmitDataUnpackingCall(pre, out, expr, intrinsic);
}
auto name = generate_builtin_name(intrinsic);
if (name.empty()) {
@@ -694,6 +696,77 @@
return true;
}
+bool GeneratorImpl::EmitDataUnpackingCall(
+ std::ostream& pre,
+ std::ostream& out,
+ ast::CallExpression* expr,
+ const semantic::Intrinsic* intrinsic) {
+ auto* param = expr->params()[0];
+ auto tmp_name = generate_name(kTempNamePrefix);
+ std::ostringstream expr_out;
+ if (!EmitExpression(pre, expr_out, param)) {
+ return false;
+ }
+ uint32_t dims = 2;
+ bool is_signed = false;
+ uint32_t scale = 65535;
+ if (intrinsic->Type() == semantic::IntrinsicType::kUnpack4x8Snorm ||
+ intrinsic->Type() == semantic::IntrinsicType::kUnpack4x8Unorm) {
+ dims = 4;
+ scale = 255;
+ }
+ if (intrinsic->Type() == semantic::IntrinsicType::kUnpack4x8Snorm ||
+ intrinsic->Type() == semantic::IntrinsicType::kUnpack2x16Snorm) {
+ is_signed = true;
+ scale = (scale - 1) / 2;
+ }
+ switch (intrinsic->Type()) {
+ case semantic::IntrinsicType::kUnpack4x8Snorm:
+ case semantic::IntrinsicType::kUnpack2x16Snorm: {
+ auto tmp_name2 = generate_name(kTempNamePrefix);
+ pre << "int " << tmp_name2 << " = int(" << expr_out.str() << ");\n";
+ // Perform sign extension on the converted values.
+ pre << "int" << dims << " " << tmp_name << " = int" << dims << "(";
+ if (dims == 2) {
+ pre << tmp_name2 << " << 16, " << tmp_name2 << ") >> 16";
+ } else {
+ pre << tmp_name2 << " << 24, " << tmp_name2 << " << 16, " << tmp_name2
+ << " << 8, " << tmp_name2 << ") >> 24";
+ }
+ pre << ";\n";
+ out << "clamp(float" << dims << "(" << tmp_name << ") / " << scale
+ << ".0, " << (is_signed ? "-1.0" : "0.0") << ", 1.0)";
+ break;
+ }
+ case semantic::IntrinsicType::kUnpack4x8Unorm:
+ case semantic::IntrinsicType::kUnpack2x16Unorm: {
+ auto tmp_name2 = generate_name(kTempNamePrefix);
+ pre << "uint " << tmp_name2 << " = " << expr_out.str() << ";\n";
+ pre << "uint" << dims << " " << tmp_name << " = uint" << dims << "(";
+ pre << tmp_name2 << " & " << (dims == 2 ? "0xffff" : "0xff") << ", ";
+ if (dims == 4) {
+ pre << "(" << tmp_name2 << " >> " << (32 / dims) << ") & 0xff, ("
+ << tmp_name2 << " >> 16) & 0xff, " << tmp_name2 << " >> 24";
+ } else {
+ pre << tmp_name2 << " >> " << (32 / dims);
+ }
+ pre << ");\n";
+ out << "float" << dims << "(" << tmp_name << ") / " << scale << ".0";
+ break;
+ }
+ case semantic::IntrinsicType::kUnpack2x16Float:
+ pre << "uint " << tmp_name << " = " << expr_out.str() << ";\n";
+ out << "f16tof32(uint2(" << tmp_name << " & 0xffff, " << tmp_name
+ << " >> 16))";
+ break;
+ default:
+ error_ = "Internal error: unhandled data packing intrinsic";
+ return false;
+ }
+
+ return true;
+}
+
bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
std::ostream& out,
ast::CallExpression* expr,
diff --git a/src/writer/hlsl/generator_impl.h b/src/writer/hlsl/generator_impl.h
index 805a0ce..23ac598 100644
--- a/src/writer/hlsl/generator_impl.h
+++ b/src/writer/hlsl/generator_impl.h
@@ -169,6 +169,16 @@
std::ostream& out,
ast::CallExpression* expr,
const semantic::Intrinsic* intrinsic);
+ /// Handles generating a call to data unpacking intrinsic
+ /// @param pre the preamble of the expression stream
+ /// @param out the output of the expression stream
+ /// @param expr the call expression
+ /// @param intrinsic the semantic information for the texture intrinsic
+ /// @returns true if the call expression is emitted
+ bool EmitDataUnpackingCall(std::ostream& pre,
+ std::ostream& out,
+ ast::CallExpression* expr,
+ const semantic::Intrinsic* intrinsic);
/// Handles a case statement
/// @param out the output stream
/// @param stmt the statement
diff --git a/src/writer/hlsl/generator_impl_intrinsic_test.cc b/src/writer/hlsl/generator_impl_intrinsic_test.cc
index 2b32853..4ec2683 100644
--- a/src/writer/hlsl/generator_impl_intrinsic_test.cc
+++ b/src/writer/hlsl/generator_impl_intrinsic_test.cc
@@ -330,7 +330,7 @@
EXPECT_THAT(result(), HasSubstr("(_tint_tmp.x | _tint_tmp.y << 16)"));
}
-TEST_F(HlslGeneratorImplTest_Intrinsic, Pack2x16float) {
+TEST_F(HlslGeneratorImplTest_Intrinsic, Pack2x16Float) {
auto* call = Call("pack2x16float", "p1");
Global("p1", ast::StorageClass::kPrivate, ty.vec2<f32>());
WrapInFunction(call);
@@ -342,6 +342,85 @@
EXPECT_THAT(result(), HasSubstr("(_tint_tmp.x | _tint_tmp.y << 16)"));
}
+TEST_F(HlslGeneratorImplTest_Intrinsic, Unpack4x8Snorm) {
+ auto* call = Call("unpack4x8snorm", "p1");
+ Global("p1", ast::StorageClass::kPrivate, ty.u32());
+ WrapInFunction(call);
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+ ASSERT_TRUE(gen.EmitExpression(pre, out, call)) << gen.error();
+ EXPECT_THAT(pre_result(), HasSubstr("int _tint_tmp_0 = int(p1);"));
+ EXPECT_THAT(pre_result(),
+ HasSubstr("int4 _tint_tmp = int4(_tint_tmp_0 << 24, _tint_tmp_0 "
+ "<< 16, _tint_tmp_0 << 8, _tint_tmp_0) >> 24;"));
+ EXPECT_THAT(result(),
+ HasSubstr("clamp(float4(_tint_tmp) / 127.0, -1.0, 1.0)"));
+}
+
+TEST_F(HlslGeneratorImplTest_Intrinsic, Unpack4x8Unorm) {
+ auto* call = Call("unpack4x8unorm", "p1");
+ Global("p1", ast::StorageClass::kPrivate, ty.u32());
+ WrapInFunction(call);
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+ ASSERT_TRUE(gen.EmitExpression(pre, out, call)) << gen.error();
+ EXPECT_THAT(pre_result(), HasSubstr("uint _tint_tmp_0 = p1;"));
+ EXPECT_THAT(
+ pre_result(),
+ HasSubstr("uint4 _tint_tmp = uint4(_tint_tmp_0 & 0xff, (_tint_tmp_0 >> "
+ "8) & 0xff, (_tint_tmp_0 >> 16) & 0xff, _tint_tmp_0 >> 24);"));
+ EXPECT_THAT(result(), HasSubstr("float4(_tint_tmp) / 255.0"));
+}
+
+TEST_F(HlslGeneratorImplTest_Intrinsic, Unpack2x16Snorm) {
+ auto* call = Call("unpack2x16snorm", "p1");
+ Global("p1", ast::StorageClass::kPrivate, ty.u32());
+ WrapInFunction(call);
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+ ASSERT_TRUE(gen.EmitExpression(pre, out, call)) << gen.error();
+ EXPECT_THAT(pre_result(), HasSubstr("int _tint_tmp_0 = int(p1);"));
+ EXPECT_THAT(
+ pre_result(),
+ HasSubstr(
+ "int2 _tint_tmp = int2(_tint_tmp_0 << 16, _tint_tmp_0) >> 16;"));
+ EXPECT_THAT(result(),
+ HasSubstr("clamp(float2(_tint_tmp) / 32767.0, -1.0, 1.0)"));
+}
+
+TEST_F(HlslGeneratorImplTest_Intrinsic, Unpack2x16Unorm) {
+ auto* call = Call("unpack2x16unorm", "p1");
+ Global("p1", ast::StorageClass::kPrivate, ty.u32());
+ WrapInFunction(call);
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+ ASSERT_TRUE(gen.EmitExpression(pre, out, call)) << gen.error();
+ EXPECT_THAT(pre_result(), HasSubstr("uint _tint_tmp_0 = p1;"));
+ EXPECT_THAT(
+ pre_result(),
+ HasSubstr(
+ "uint2 _tint_tmp = uint2(_tint_tmp_0 & 0xffff, _tint_tmp_0 >> 16);"));
+ EXPECT_THAT(result(), HasSubstr("float2(_tint_tmp) / 65535.0"));
+}
+
+TEST_F(HlslGeneratorImplTest_Intrinsic, Unpack2x16Float) {
+ auto* call = Call("unpack2x16float", "p1");
+ Global("p1", ast::StorageClass::kPrivate, ty.u32());
+ WrapInFunction(call);
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+ ASSERT_TRUE(gen.EmitExpression(pre, out, call)) << gen.error();
+ EXPECT_THAT(pre_result(), HasSubstr("uint _tint_tmp = p1;"));
+ EXPECT_THAT(
+ result(),
+ HasSubstr("f16tof32(uint2(_tint_tmp & 0xffff, _tint_tmp >> 16))"));
+}
+
} // namespace
} // namespace hlsl
} // namespace writer
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index 1f71561..5421a64 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -449,9 +449,14 @@
if (intrinsic->IsTexture()) {
return EmitTextureCall(expr, intrinsic);
}
- if (intrinsic->Type() == semantic::IntrinsicType::kPack2x16Float) {
+ if (intrinsic->Type() == semantic::IntrinsicType::kPack2x16Float ||
+ intrinsic->Type() == semantic::IntrinsicType::kUnpack2x16Float) {
make_indent();
- out_ << "as_type<uint>(half2(";
+ if (intrinsic->Type() == semantic::IntrinsicType::kPack2x16Float) {
+ out_ << "as_type<uint>(half2(";
+ } else {
+ out_ << "float2(as_type<half2>(";
+ }
if (!EmitExpression(expr->params()[0])) {
return false;
}
@@ -900,6 +905,18 @@
case semantic::IntrinsicType::kInverseSqrt:
out += "rsqrt";
break;
+ case semantic::IntrinsicType::kUnpack4x8Snorm:
+ out += "unpack_snorm4x8_to_float";
+ break;
+ case semantic::IntrinsicType::kUnpack4x8Unorm:
+ out += "unpack_unorm4x8_to_float";
+ break;
+ case semantic::IntrinsicType::kUnpack2x16Snorm:
+ out += "unpack_snorm2x16_to_float";
+ break;
+ case semantic::IntrinsicType::kUnpack2x16Unorm:
+ out += "unpack_unorm2x16_to_float";
+ break;
default:
error_ = "Unknown import method: " + std::string(intrinsic->str());
return "";
diff --git a/src/writer/msl/generator_impl_intrinsic_test.cc b/src/writer/msl/generator_impl_intrinsic_test.cc
index 56b194a..3873b4c 100644
--- a/src/writer/msl/generator_impl_intrinsic_test.cc
+++ b/src/writer/msl/generator_impl_intrinsic_test.cc
@@ -157,6 +157,11 @@
case IntrinsicType::kPack4x8Snorm:
case IntrinsicType::kPack4x8Unorm:
return builder->Call(str.str(), "f4");
+ case IntrinsicType::kUnpack4x8Snorm:
+ case IntrinsicType::kUnpack4x8Unorm:
+ case IntrinsicType::kUnpack2x16Snorm:
+ case IntrinsicType::kUnpack2x16Unorm:
+ return builder->Call(str.str(), "u1");
default:
break;
}
@@ -174,6 +179,7 @@
Global("f2", ast::StorageClass::kFunction, ty.vec2<float>());
Global("f3", ast::StorageClass::kFunction, ty.vec3<float>());
Global("f4", ast::StorageClass::kFunction, ty.vec4<float>());
+ Global("u1", ast::StorageClass::kFunction, ty.u32());
Global("u2", ast::StorageClass::kFunction, ty.vec2<unsigned int>());
Global("b2", ast::StorageClass::kFunction, ty.vec2<bool>());
Global("m2x2", ast::StorageClass::kFunction, ty.mat2x2<float>());
@@ -276,7 +282,15 @@
IntrinsicData{IntrinsicType::kStep, ParamType::kF32, "metal::step"},
IntrinsicData{IntrinsicType::kTan, ParamType::kF32, "metal::tan"},
IntrinsicData{IntrinsicType::kTanh, ParamType::kF32, "metal::tanh"},
- IntrinsicData{IntrinsicType::kTrunc, ParamType::kF32, "metal::trunc"}));
+ IntrinsicData{IntrinsicType::kTrunc, ParamType::kF32, "metal::trunc"},
+ IntrinsicData{IntrinsicType::kUnpack4x8Snorm, ParamType::kU32,
+ "metal::unpack_snorm4x8_to_float"},
+ IntrinsicData{IntrinsicType::kUnpack4x8Unorm, ParamType::kU32,
+ "metal::unpack_unorm4x8_to_float"},
+ IntrinsicData{IntrinsicType::kUnpack2x16Snorm, ParamType::kU32,
+ "metal::unpack_snorm2x16_to_float"},
+ IntrinsicData{IntrinsicType::kUnpack2x16Unorm, ParamType::kU32,
+ "metal::unpack_unorm2x16_to_float"}));
TEST_F(MslGeneratorImplTest, Intrinsic_Call) {
Global("param1", ast::StorageClass::kFunction, ty.vec2<f32>());
@@ -304,6 +318,18 @@
EXPECT_EQ(gen.result(), " as_type<uint>(half2(p1))");
}
+TEST_F(MslGeneratorImplTest, Unpack2x16Float) {
+ auto* call = Call("unpack2x16float", "p1");
+ Global("p1", ast::StorageClass::kFunction, ty.u32());
+ WrapInFunction(call);
+
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+ ASSERT_TRUE(gen.EmitExpression(call)) << gen.error();
+ EXPECT_EQ(gen.result(), " float2(as_type<half2>(p1))");
+}
+
} // namespace
} // namespace msl
} // namespace writer
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 6bdeab5..de8a747 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -284,6 +284,16 @@
return GLSLstd450Tanh;
case IntrinsicType::kTrunc:
return GLSLstd450Trunc;
+ case IntrinsicType::kUnpack4x8Snorm:
+ return GLSLstd450UnpackSnorm4x8;
+ case IntrinsicType::kUnpack4x8Unorm:
+ return GLSLstd450UnpackUnorm4x8;
+ case IntrinsicType::kUnpack2x16Snorm:
+ return GLSLstd450UnpackSnorm2x16;
+ case IntrinsicType::kUnpack2x16Unorm:
+ return GLSLstd450UnpackUnorm2x16;
+ case IntrinsicType::kUnpack2x16Float:
+ return GLSLstd450UnpackHalf2x16;
default:
break;
}
diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc
index 1095e34..b20e553 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -1506,6 +1506,131 @@
Validate(b);
}
+using Intrinsic_Builtin_DataPacking_Test =
+ IntrinsicBuilderTestWithParam<IntrinsicData>;
+TEST_P(Intrinsic_Builtin_DataPacking_Test, Binary) {
+ auto param = GetParam();
+
+ bool pack4 = param.name == "pack4x8snorm" || param.name == "pack4x8unorm";
+ auto* call = pack4 ? Call(param.name, vec4<float>(1.0f, 1.0f, 1.0f, 1.0f))
+ : Call(param.name, vec2<float>(1.0f, 1.0f));
+ WrapInFunction(call);
+
+ auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
+ ast::StatementList{}, ast::FunctionDecorationList{});
+
+ spirv::Builder& b = Build();
+
+ ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
+
+ EXPECT_EQ(b.GenerateCallExpression(call), 5u) << b.error();
+ if (pack4) {
+ EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeInt 32 0
+%9 = OpTypeFloat 32
+%8 = OpTypeVector %9 4
+%10 = OpConstant %9 1
+%11 = OpConstantComposite %8 %10 %10 %10 %10
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 )" + param.op +
+ R"( %11
+OpReturn
+OpFunctionEnd
+)");
+ } else {
+ EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeInt 32 0
+%9 = OpTypeFloat 32
+%8 = OpTypeVector %9 2
+%10 = OpConstant %9 1
+%11 = OpConstantComposite %8 %10 %10
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 )" + param.op +
+ R"( %11
+OpReturn
+OpFunctionEnd
+)");
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ IntrinsicBuilderTest,
+ Intrinsic_Builtin_DataPacking_Test,
+ testing::Values(IntrinsicData{"pack4x8snorm", "PackSnorm4x8"},
+ IntrinsicData{"pack4x8unorm", "PackUnorm4x8"},
+ IntrinsicData{"pack2x16snorm", "PackSnorm2x16"},
+ IntrinsicData{"pack2x16unorm", "PackUnorm2x16"},
+ IntrinsicData{"pack2x16float", "PackHalf2x16"}));
+
+using Intrinsic_Builtin_DataUnpacking_Test =
+ IntrinsicBuilderTestWithParam<IntrinsicData>;
+TEST_P(Intrinsic_Builtin_DataUnpacking_Test, Binary) {
+ auto param = GetParam();
+
+ bool pack4 = param.name == "unpack4x8snorm" || param.name == "unpack4x8unorm";
+ auto* call = Call(param.name, 1u);
+ WrapInFunction(call);
+
+ auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
+ ast::StatementList{}, ast::FunctionDecorationList{});
+
+ spirv::Builder& b = Build();
+
+ ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
+
+ EXPECT_EQ(b.GenerateCallExpression(call), 5u) << b.error();
+ if (pack4) {
+ EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeFloat 32
+%6 = OpTypeVector %7 4
+%9 = OpTypeInt 32 0
+%10 = OpConstant %9 1
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %8 )" + param.op +
+ R"( %10
+OpReturn
+OpFunctionEnd
+)");
+ } else {
+ EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeFloat 32
+%6 = OpTypeVector %7 2
+%9 = OpTypeInt 32 0
+%10 = OpConstant %9 1
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %8 )" + param.op +
+ R"( %10
+OpReturn
+OpFunctionEnd
+)");
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ IntrinsicBuilderTest,
+ Intrinsic_Builtin_DataUnpacking_Test,
+ testing::Values(IntrinsicData{"unpack4x8snorm", "UnpackSnorm4x8"},
+ IntrinsicData{"unpack4x8unorm", "UnpackUnorm4x8"},
+ IntrinsicData{"unpack2x16snorm", "UnpackSnorm2x16"},
+ IntrinsicData{"unpack2x16unorm", "UnpackUnorm2x16"},
+ IntrinsicData{"unpack2x16float", "UnpackHalf2x16"}));
+
} // namespace
} // namespace spirv
} // namespace writer