Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions impeller/compiler/reflector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,29 @@ std::vector<StructMember> Reflector::ReadStructMembers(
continue;
}

// Tightly packed Half Float Vector.
if (member.basetype == spirv_cross::SPIRType::BaseType::Half && //
member.width == sizeof(float) * 4 && //
member.columns == 1 && //
member.vecsize == 4 //
) {
uint32_t stride =
GetArrayStride<sizeof(HalfVector4)>(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);
Expand Down
4 changes: 2 additions & 2 deletions impeller/entity/shaders/solid_fill.frag
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
#include <impeller/types.glsl>

uniform FragInfo {
vec4 color;
f16vec4 color;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this require #extension GL_EXT_shader_16bit_storage : require up top?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not, I'm not actually using a f16vec4 on glsl, here is the compiled shader:

#version 100
precision mediump float;
precision highp int;

struct FragInfo
{
    vec4 color;
};

uniform FragInfo frag_info;

void main()
{
    gl_FragData[0] = frag_info.color;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh - but this is all in types.glsl too

}
frag_info;

out vec4 frag_color;
out f16vec4 frag_color;

void main() {
frag_color = frag_info.color;
Expand Down
1 change: 1 addition & 0 deletions impeller/geometry/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ impeller_component("geometry") {
"quaternion.h",
"rect.cc",
"rect.h",
"scalar.cc",
"scalar.h",
"shear.cc",
"shear.h",
Expand Down
1 change: 1 addition & 0 deletions impeller/geometry/point.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ using IPoint = TPoint<int64_t>;
using IPoint32 = TPoint<int32_t>;
using UintPoint32 = TPoint<uint32_t>;
using Vector2 = Point;
using HalfVector2 = TPoint<Half>;

} // namespace impeller

Expand Down
40 changes: 40 additions & 0 deletions impeller/geometry/scalar.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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.

#include "scalar.h"

namespace impeller {

uint16_t ScalarToHalf(Scalar f) {
uint32_t x = *reinterpret_cast<const uint32_t*>(&f);
uint32_t sign = (unsigned short)(x >> 31);
uint32_t mantissa;
uint32_t exp;
uint16_t hf;

mantissa = x & ((1 << 23) - 1);
exp = x & (0xFF << 23);
if (exp >= 0x47800000) {
// check if the original number is a NaN
if (mantissa && (exp == (0xFF << 23))) {
// single precision NaN
mantissa = (1 << 23) - 1;
} else {
// half-float will be Inf
mantissa = 0;
}
hf = (((uint16_t)sign) << 15) | (uint16_t)((0x1F << 10)) |
(uint16_t)(mantissa >> 13);
}
// check if exponent is <= -15
else if (exp <= 0x38000000) {
hf = 0; // too small to be represented
} else {
hf = (((uint16_t)sign) << 15) | (uint16_t)((exp - 0x38000000) >> 13) |
(uint16_t)(mantissa >> 13);
}
return hf;
}

} // namespace impeller
8 changes: 8 additions & 0 deletions impeller/geometry/scalar.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

#include <cfloat>
#include <cstdint>
#include <type_traits>
#include <valarray>

Expand All @@ -13,6 +14,7 @@
namespace impeller {

using Scalar = float;
using Half = uint16_t;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this different from the _Float16 C11 extension? I also see __fp16.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL


template <class T, class = std::enable_if_t<std::is_arithmetic_v<T>>>
constexpr T Absolute(const T& val) {
Expand Down Expand Up @@ -52,4 +54,10 @@ struct Degrees {
};
};

/// @brief Convert a scalar to a half precision float.
///
/// Adapted from
/// https://developer.android.com/games/optimize/vertex-data-management .
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should document the behavior of this and how it handles precision loss, and add some tests that assert it.

Specifically:

  • What happens when the f is greater than the max finite value or less than the lowest finite value of a half float?
  • What happens when f is infinity/-infinity/nan?
  • What happens to very tiny values of f?

uint16_t ScalarToHalf(Scalar f);

} // namespace impeller
6 changes: 6 additions & 0 deletions impeller/geometry/vector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,10 @@ std::string Vector4::ToString() const {
return stream.str();
}

std::string HalfVector4::ToString() const {
std::stringstream stream;
stream << "{" << x << ", " << y << ", " << z << ", " << w << "}";
return stream.str();
}

} // namespace impeller
43 changes: 43 additions & 0 deletions impeller/geometry/vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,43 @@ struct Vector4 {
static_assert(sizeof(Vector3) == 3 * sizeof(Scalar));
static_assert(sizeof(Vector4) == 4 * sizeof(Scalar));

struct HalfVector4 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could convert Vector3 and others to be TVector3 with template types for floats or half-floats. Then using Vector3 = TVector<Scalar>; and using HalfVector3 = TVector<Half>. Will significantly dry up this code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh good idea

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one argument against doing this is that we don't really want to use half precision anywhere except for in the shaders.

union {
struct {
Half x = 0.0;
Half y = 0.0;
Half z = 0.0;
Half w = 1.0;
};
Half e[4];
};

constexpr HalfVector4() {}

constexpr HalfVector4(const Color& c)
: x(ScalarToHalf(c.red)),
y(ScalarToHalf(c.green)),
z(ScalarToHalf(c.blue)),
w(ScalarToHalf(c.alpha)) {}

constexpr HalfVector4(Half x, Half y, Half z, Half w)
: x(x), y(y), z(z), w(w) {}

constexpr HalfVector4(const HalfVector2& p) : x(p.x), y(p.y) {}

constexpr bool operator==(const HalfVector4& v) const {
return (x == v.x) && (y == v.y) && (z == v.z) && (w == v.w);
}

constexpr bool operator!=(const HalfVector4& v) const {
return (x != v.x) || (y != v.y) || (z != v.z) || (w != v.w);
}

std::string ToString() const;
};

static_assert(sizeof(HalfVector4) == 4 * sizeof(Half));

} // namespace impeller

namespace std {
Expand All @@ -302,4 +339,10 @@ inline std::ostream& operator<<(std::ostream& out, const impeller::Vector4& p) {
return out;
}

inline std::ostream& operator<<(std::ostream& out,
const impeller::HalfVector4& p) {
out << "(" << p.x << ", " << p.y << ", " << p.z << ", " << p.w << ")";
return out;
}

} // namespace std