diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 9cf5630e20596..e890eabfa2fe2 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1288,6 +1288,7 @@ ORIGIN: ../../../flutter/impeller/geometry/constants.h + ../../../flutter/LICENS ORIGIN: ../../../flutter/impeller/geometry/geometry_benchmarks.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/geometry/gradient.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/geometry/gradient.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/geometry/half.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/geometry/matrix.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/geometry/matrix.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/geometry/matrix_decomposition.cc + ../../../flutter/LICENSE @@ -3851,6 +3852,7 @@ FILE: ../../../flutter/impeller/geometry/constants.h FILE: ../../../flutter/impeller/geometry/geometry_benchmarks.cc FILE: ../../../flutter/impeller/geometry/gradient.cc FILE: ../../../flutter/impeller/geometry/gradient.h +FILE: ../../../flutter/impeller/geometry/half.h FILE: ../../../flutter/impeller/geometry/matrix.cc FILE: ../../../flutter/impeller/geometry/matrix.h FILE: ../../../flutter/impeller/geometry/matrix_decomposition.cc diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 09ad507111f9d..93863aa03c234 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -18,6 +18,7 @@ #include "impeller/compiler/code_gen_template.h" #include "impeller/compiler/uniform_sorter.h" #include "impeller/compiler/utilities.h" +#include "impeller/geometry/half.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/scalar.h" @@ -534,6 +535,11 @@ static std::optional ReadKnownScalarType( .name = "Scalar", .byte_size = sizeof(Scalar), }; + case spirv_cross::SPIRType::BaseType::Half: + return KnownType{ + .name = "Half", + .byte_size = sizeof(Half), + }; case spirv_cross::SPIRType::BaseType::UInt: return KnownType{ .name = "uint32_t", @@ -767,6 +773,75 @@ std::vector Reflector::ReadStructMembers( continue; } + // Tightly packed half Point (vec2). + if (member.basetype == spirv_cross::SPIRType::BaseType::Half && // + member.width == sizeof(Half) * 8 && // + member.columns == 1 && // + member.vecsize == 2 // + ) { + uint32_t stride = + GetArrayStride(struct_type, member, i); + uint32_t element_padding = stride - sizeof(HalfVector2); + result.emplace_back(StructMember{ + "HalfVector2", // type + BaseTypeToString(member.basetype), // basetype + GetMemberNameAtIndex(struct_type, i), // name + struct_member_offset, // offset + sizeof(HalfVector2), // size + stride * array_elements.value_or(1), // byte_length + array_elements, // array_elements + element_padding, // element_padding + }); + current_byte_offset += stride * array_elements.value_or(1); + continue; + } + + // Tightly packed Half Float Vector3. + if (member.basetype == spirv_cross::SPIRType::BaseType::Half && // + member.width == sizeof(Half) * 8 && // + member.columns == 1 && // + member.vecsize == 3 // + ) { + uint32_t stride = + GetArrayStride(struct_type, member, i); + uint32_t element_padding = stride - sizeof(HalfVector3); + result.emplace_back(StructMember{ + "HalfVector3", // type + BaseTypeToString(member.basetype), // basetype + GetMemberNameAtIndex(struct_type, i), // name + struct_member_offset, // offset + sizeof(HalfVector3), // size + stride * array_elements.value_or(1), // byte_length + array_elements, // array_elements + element_padding, // element_padding + }); + current_byte_offset += stride * array_elements.value_or(1); + continue; + } + + // Tightly packed Half Float Vector4. + if (member.basetype == spirv_cross::SPIRType::BaseType::Half && // + member.width == sizeof(Half) * 8 && // + member.columns == 1 && // + member.vecsize == 4 // + ) { + uint32_t stride = + GetArrayStride(struct_type, member, i); + uint32_t element_padding = stride - sizeof(HalfVector4); + result.emplace_back(StructMember{ + "HalfVector4", // type + BaseTypeToString(member.basetype), // basetype + GetMemberNameAtIndex(struct_type, i), // name + struct_member_offset, // offset + sizeof(HalfVector4), // size + stride * array_elements.value_or(1), // byte_length + array_elements, // array_elements + element_padding, // element_padding + }); + current_byte_offset += stride * array_elements.value_or(1); + continue; + } + // Other isolated scalars (like bool, int, float/Scalar, etc..). { auto maybe_known_type = ReadKnownScalarType(member.basetype); diff --git a/impeller/compiler/shader_lib/impeller/types.glsl b/impeller/compiler/shader_lib/impeller/types.glsl index 87c7eee666c69..0cc2fee2aa332 100644 --- a/impeller/compiler/shader_lib/impeller/types.glsl +++ b/impeller/compiler/shader_lib/impeller/types.glsl @@ -6,8 +6,10 @@ #define TYPES_GLSL_ #extension GL_AMD_gpu_shader_half_float : enable +#extension GL_AMD_gpu_shader_half_float_fetch : enable +#extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable -#ifndef IMPELLER_TARGET_METAL +#ifndef IMPELLER_TARGET_METAL_IOS precision mediump sampler2D; precision mediump float; @@ -17,6 +19,7 @@ precision mediump float; #define f16vec3 vec3 #define f16vec4 vec4 #define f16mat4 mat4 +#define f16sampler2D sampler2D #endif // IMPELLER_TARGET_METAL diff --git a/impeller/entity/shaders/solid_fill.frag b/impeller/entity/shaders/solid_fill.frag index 28c7776da9e1d..4714def3aacf8 100644 --- a/impeller/entity/shaders/solid_fill.frag +++ b/impeller/entity/shaders/solid_fill.frag @@ -5,11 +5,11 @@ #include uniform FragInfo { - vec4 color; + f16vec4 color; } frag_info; -out vec4 frag_color; +out f16vec4 frag_color; void main() { frag_color = frag_info.color; diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 8dee5288196f8..cba9c78afe896 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -48,6 +48,7 @@ impeller_shaders("shader_fixtures") { "simple.vert", "test_texture.frag", "test_texture.vert", + "half.frag", ] if (impeller_enable_opengles) { @@ -55,6 +56,7 @@ impeller_shaders("shader_fixtures") { "sample.comp", "stage1.comp", "stage2.comp", + "half.frag", ] } } diff --git a/impeller/fixtures/half.frag b/impeller/fixtures/half.frag new file mode 100644 index 0000000000000..527476d8b6eb8 --- /dev/null +++ b/impeller/fixtures/half.frag @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#extension GL_AMD_gpu_shader_half_float : enable +#extension GL_AMD_gpu_shader_half_float_fetch : enable +#extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable + +uniform FragInfo { + float16_t half_1; + f16vec2 half_2; + f16vec3 half_3; + f16vec4 half_4; +} +frag_info; + +out vec4 frag_color; + +void main() { + frag_color = vec4(0); +} diff --git a/impeller/geometry/BUILD.gn b/impeller/geometry/BUILD.gn index a1b511ce9f41b..9aaa4e8ec8149 100644 --- a/impeller/geometry/BUILD.gn +++ b/impeller/geometry/BUILD.gn @@ -12,6 +12,7 @@ impeller_component("geometry") { "constants.h", "gradient.cc", "gradient.h", + "half.h", "matrix.cc", "matrix.h", "matrix_decomposition.cc", diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 0b1e963571e4c..143de168ef293 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -7,9 +7,11 @@ #include #include +#include "flutter/fml/build_config.h" #include "flutter/testing/testing.h" #include "impeller/geometry/constants.h" #include "impeller/geometry/gradient.h" +#include "impeller/geometry/half.h" #include "impeller/geometry/path.h" #include "impeller/geometry/path_builder.h" #include "impeller/geometry/path_component.h" @@ -2090,5 +2092,39 @@ TEST(GeometryTest, Gradient) { } } +TEST(GeometryTest, HalfConversions) { +#ifdef FML_OS_WIN + GTEST_SKIP() << "Half-precision floats (IEEE 754) are not portable and " + "unavailable on Windows."; +#else + ASSERT_EQ(ScalarToHalf(0.0), 0.0f16); + ASSERT_EQ(ScalarToHalf(0.05), 0.05f16); + ASSERT_EQ(ScalarToHalf(2.43), 2.43f16); + ASSERT_EQ(ScalarToHalf(-1.45), -1.45f16); + + // 65504 is the largest possible half. + ASSERT_EQ(ScalarToHalf(65504.0f), 65504.0f16); + ASSERT_EQ(ScalarToHalf(65504.0f + 1), 65504.0f16); + + // Colors + ASSERT_EQ(HalfVector4(Color::Red()), + HalfVector4(1.0f16, 0.0f16, 0.0f16, 1.0f16)); + ASSERT_EQ(HalfVector4(Color::Green()), + HalfVector4(0.0f16, 1.0f16, 0.0f16, 1.0f16)); + ASSERT_EQ(HalfVector4(Color::Blue()), + HalfVector4(0.0f16, 0.0f16, 1.0f16, 1.0f16)); + ASSERT_EQ(HalfVector4(Color::Black().WithAlpha(0)), + HalfVector4(0.0f16, 0.0f16, 0.0f16, 0.0f16)); + + ASSERT_EQ(HalfVector3(Vector3(4.0, 6.0, -1.0)), + HalfVector3(4.0f16, 6.0f16, -1.0f16)); + ASSERT_EQ(HalfVector2(Vector2(4.0, 6.0)), HalfVector2(4.0f16, 6.0f16)); + + ASSERT_EQ(Half(0.5f), Half(0.5f16)); + ASSERT_EQ(Half(0.5), Half(0.5f16)); + ASSERT_EQ(Half(5), Half(5.0f16)); +#endif // FML_OS_WIN +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/half.h b/impeller/geometry/half.h new file mode 100644 index 0000000000000..ed81e16a110f4 --- /dev/null +++ b/impeller/geometry/half.h @@ -0,0 +1,188 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/build_config.h" + +#include "impeller/geometry/color.h" +#include "impeller/geometry/point.h" +#include "impeller/geometry/scalar.h" +#include "impeller/geometry/vector.h" + +#ifdef FML_OS_WIN +using InternalHalf = uint16_t; +#else +using InternalHalf = _Float16; +#endif + +namespace impeller { + +/// @brief Convert a scalar to a half precision float. +/// +/// See also: https://clang.llvm.org/docs/LanguageExtensions.html +/// This is not currently supported on Windows toolchains. +inline constexpr InternalHalf ScalarToHalf(Scalar f) { +#ifdef FML_OS_WIN + return static_cast(0); +#else + return static_cast(f); +#endif +} + +/// @brief A storage only class for half precision floating point. +struct Half { + InternalHalf x = 0; + + constexpr Half() {} + + constexpr Half(double value) : x(ScalarToHalf(static_cast(value))) {} + + constexpr Half(Scalar value) : x(ScalarToHalf(value)) {} + + constexpr Half(int value) : x(ScalarToHalf(static_cast(value))) {} + + constexpr Half(InternalHalf x) : x(x) {} + + constexpr bool operator==(const Half& v) const { return v.x == x; } + + constexpr bool operator!=(const Half& v) const { return v.x != x; } +}; + +/// @brief A storage only class for half precision floating point vector 4. +struct HalfVector4 { + union { + struct { + InternalHalf x = 0; + InternalHalf y = 0; + InternalHalf z = 0; + InternalHalf w = 0; + }; + InternalHalf e[4]; + }; + + constexpr HalfVector4() {} + + constexpr HalfVector4(const Color& a) + : x(ScalarToHalf(a.red)), + y(ScalarToHalf(a.green)), + z(ScalarToHalf(a.blue)), + w(ScalarToHalf(a.alpha)) {} + + constexpr HalfVector4(const Vector4& a) + : x(ScalarToHalf(a.x)), + y(ScalarToHalf(a.y)), + z(ScalarToHalf(a.z)), + w(ScalarToHalf(a.w)) {} + + constexpr HalfVector4(InternalHalf x, + InternalHalf y, + InternalHalf z, + InternalHalf w) + : x(x), y(y), z(z), w(w) {} + + constexpr bool operator==(const HalfVector4& v) const { + return v.x == x && v.y == y && v.z == z && v.w == w; + } + + constexpr bool operator!=(const HalfVector4& v) const { + return v.x != x || v.y != y || v.z != z || v.w != w; + } +}; + +/// @brief A storage only class for half precision floating point vector 3. +struct HalfVector3 { + union { + struct { + InternalHalf x = 0; + InternalHalf y = 0; + InternalHalf z = 0; + }; + InternalHalf e[3]; + }; + + constexpr HalfVector3() {} + + constexpr HalfVector3(const Vector3& a) + : x(ScalarToHalf(a.x)), y(ScalarToHalf(a.y)), z(ScalarToHalf(a.z)) {} + + constexpr HalfVector3(InternalHalf x, InternalHalf y, InternalHalf z) + : x(x), y(y), z(z) {} + + constexpr bool operator==(const HalfVector3& v) const { + return v.x == x && v.y == y && v.z == z; + } + + constexpr bool operator!=(const HalfVector3& v) const { + return v.x != x || v.y != y || v.z != z; + } +}; + +/// @brief A storage only class for half precision floating point vector 2. +struct HalfVector2 { + union { + struct { + InternalHalf x = 0; + InternalHalf y = 0; + }; + InternalHalf e[2]; + }; + + constexpr HalfVector2() {} + + constexpr HalfVector2(const Vector2& a) + : x(ScalarToHalf(a.x)), y(ScalarToHalf(a.y)) {} + + constexpr HalfVector2(InternalHalf x, InternalHalf y) : x(x), y(y){}; + + constexpr bool operator==(const HalfVector2& v) const { + return v.x == x && v.y == y; + } + + constexpr bool operator!=(const HalfVector2& v) const { + return v.x != x || v.y != y; + } +}; + +static_assert(sizeof(Half) == sizeof(uint16_t)); +static_assert(sizeof(HalfVector2) == 2 * sizeof(Half)); +static_assert(sizeof(HalfVector3) == 3 * sizeof(Half)); +static_assert(sizeof(HalfVector4) == 4 * sizeof(Half)); + +} // namespace impeller + +namespace std { + +inline std::ostream& operator<<(std::ostream& out, const impeller::Half& p) { + out << "(" << static_cast(p.x) << ")"; + return out; +} + +inline std::ostream& operator<<(std::ostream& out, + const impeller::HalfVector2& p) { + out << "(" << static_cast(p.x) << ", " + << static_cast(p.y) << ")"; + return out; +} + +inline std::ostream& operator<<(std::ostream& out, + const impeller::HalfVector3& p) { + out << "(" << static_cast(p.x) << ", " + << static_cast(p.y) << ", " + << static_cast(p.z) << ")"; + return out; +} + +inline std::ostream& operator<<(std::ostream& out, + const impeller::HalfVector4& p) { + out << "(" << static_cast(p.x) << ", " + << static_cast(p.y) << ", " + << static_cast(p.z) << ", " + << static_cast(p.w) << ")"; + return out; +} + +} // namespace std diff --git a/impeller/renderer/shader_types.h b/impeller/renderer/shader_types.h index b29bffd6f1260..8767924b42271 100644 --- a/impeller/renderer/shader_types.h +++ b/impeller/renderer/shader_types.h @@ -11,6 +11,7 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/logging.h" +#include "impeller/geometry/half.h" #include "impeller/geometry/matrix.h" #include "impeller/runtime_stage/runtime_types.h"