compat: convert a swap record's brightness to 16.16 nits
The A16 kext now takes the panel brightness from the swap record IOMobileFramebufferSwapSetBrightness fills: an IEEE double in nits, unaligned, straight from a client. edp_bl_double_to_q16 turns its bits into 16.16 nits in integer arithmetic, rounded to nearest and capped, refuses NaN, infinities and negative values (which name no brightness), and maps zero and subnormals to 0 (the darkest request). The model test checks it bit-exact against the host's double arithmetic for chosen values and for every exponent with a spread of mantissas, and four mutations (NaN or negative accepted, truncation, no cap) must fail it. Header-only and unused by the monitor: the SNAP_EDP_BACKLIGHT monitors built with run next1b's flags, with and without the define, are byte-identical to org next's.
This commit is contained in:
1 parent
7e8bf82b3a
commit
3c995cc8d8
3 files changed
+77
No files matched your search
@@ -233,6 +233,27 @@ static inline uint32_t edp_bl_nits_to_slider(uint64_t nits_q16, uint64_t lo_q16,
|
|||||||
if (nits_q16 >= hi_q16) return EDP_BL_SLIDER_MAX;
|
if (nits_q16 >= hi_q16) return EDP_BL_SLIDER_MAX;
|
||||||
return (uint32_t)(((nits_q16 - lo_q16) * EDP_BL_SLIDER_MAX + (hi_q16 - lo_q16) / 2) / (hi_q16 - lo_q16));
|
return (uint32_t)(((nits_q16 - lo_q16) * EDP_BL_SLIDER_MAX + (hi_q16 - lo_q16) / 2) / (hi_q16 - lo_q16));
|
||||||
}
|
}
|
||||||
|
/* A swap record's brightness (an IEEE 754 binary64 in nits, as IOMobileFramebufferSwapSetBrightness stores it) as
|
||||||
|
* 16.16 nits, rounded to nearest and capped at cap_q16, in integer arithmetic only: the bits come straight from a
|
||||||
|
* client's record. 0 with *q16 set; -1 for a NaN, an infinity or a negative value, which name no brightness. Zero and
|
||||||
|
* subnormals are 0 nits (the darkest request). */
|
||||||
|
static inline int edp_bl_double_to_q16(uint64_t bits, uint64_t cap_q16, uint64_t *q16)
|
||||||
|
{
|
||||||
|
/* A normal value is mantissa * 2^(exponent - 1075), so value * 2^16 = mantissa * 2^(exponent - 1059). */
|
||||||
|
uint32_t exponent = (uint32_t)(bits >> 52) & 0x7ffu;
|
||||||
|
uint64_t mantissa = (bits & ((1ull << 52) - 1)) | 1ull << 52, v;
|
||||||
|
if (exponent == 0x7ffu || (bits >> 63 && bits << 1)) return -1;
|
||||||
|
if (exponent == 0)
|
||||||
|
v = 0;
|
||||||
|
else if (exponent >= 1059) /* 2^36 nits or more; a mantissa below 2^53 shifted by up to 10 stays below 2^63 */
|
||||||
|
v = exponent - 1059 > 10 ? UINT64_MAX : mantissa << (exponent - 1059);
|
||||||
|
else if (1059 - exponent > 63)
|
||||||
|
v = 0;
|
||||||
|
else
|
||||||
|
v = (mantissa + (1ull << (1059 - exponent - 1))) >> (1059 - exponent);
|
||||||
|
*q16 = v > cap_q16 ? cap_q16 : v;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Allow-lists: every MMIO access and every DPCD transaction the monitor makes passes one of these ----------- */
|
/* ---- Allow-lists: every MMIO access and every DPCD transaction the monitor makes passes one of these ----------- */
|
||||||
static inline int edp_bl_mmio_read_ok(uint64_t pa)
|
static inline int edp_bl_mmio_read_ok(uint64_t pa)
|
||||||
|
|||||||
@@ -242,6 +242,54 @@ static void test_curve(void)
|
|||||||
CHECK(edp_bl_nits_to_level(edp_bl_slider_to_nits(65536, lo, Q(500)), maxn, floor, max) == max);
|
CHECK(edp_bl_nits_to_level(edp_bl_slider_to_nits(65536, lo, Q(500)), maxn, floor, max) == max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* A swap record's brightness double as 16.16 nits: bit-exact against the host's own double arithmetic, refusing what
|
||||||
|
* names no brightness, capped, and never trusting the bits to be a sensible number. */
|
||||||
|
static uint64_t bits_of(double d) { uint64_t b; memcpy(&b, &d, sizeof b); return b; }
|
||||||
|
static void test_swap_double(void)
|
||||||
|
{
|
||||||
|
const uint64_t cap = (uint64_t)EDP_BL_NITS_LIMIT_MAX << 16;
|
||||||
|
uint64_t q = 12345;
|
||||||
|
static const double exact[] = {0.0, 1.0, 5.0, 10.015, 140.0, 174.89, 384.615, 499.999, 500.0, 0.5, 1.0 / 65536,
|
||||||
|
1.5 / 65536, 0.49 / 65536, 1e-300, 999.99998};
|
||||||
|
for (unsigned i = 0; i < sizeof exact / sizeof exact[0]; i++) {
|
||||||
|
double diff;
|
||||||
|
CHECK(edp_bl_double_to_q16(bits_of(exact[i]), cap, &q) == 0);
|
||||||
|
diff = (double)q - exact[i] * 65536.0;
|
||||||
|
CHECK(diff <= 0.5 && diff >= -0.5);
|
||||||
|
}
|
||||||
|
CHECK(edp_bl_double_to_q16(bits_of(140.0), cap, &q) == 0 && q == 140ull << 16);
|
||||||
|
CHECK(edp_bl_double_to_q16(bits_of(-0.0), cap, &q) == 0 && q == 0);
|
||||||
|
CHECK(edp_bl_double_to_q16(bits_of(4.9e-324), cap, &q) == 0 && q == 0); /* subnormal */
|
||||||
|
CHECK(edp_bl_double_to_q16(bits_of(1000.5), cap, &q) == 0 && q == cap);
|
||||||
|
CHECK(edp_bl_double_to_q16(bits_of(1e300), cap, &q) == 0 && q == cap);
|
||||||
|
CHECK(edp_bl_double_to_q16(bits_of(68719476736.0), cap, &q) == 0 && q == cap); /* 2^36: the shifted branch */
|
||||||
|
CHECK(edp_bl_double_to_q16(bits_of(1e300), UINT64_MAX, &q) == 0 && q == UINT64_MAX);
|
||||||
|
CHECK(edp_bl_double_to_q16(bits_of(68719476736.0), UINT64_MAX, &q) == 0 && q == 1ull << 52);
|
||||||
|
q = 777;
|
||||||
|
CHECK(edp_bl_double_to_q16(0x7ff8000000000000ull, cap, &q) == -1 && q == 777); /* the NaN CoreAnimation logs */
|
||||||
|
CHECK(edp_bl_double_to_q16(0x7ff0000000000001ull, cap, &q) == -1); /* signalling NaN */
|
||||||
|
CHECK(edp_bl_double_to_q16(0x7ff0000000000000ull, cap, &q) == -1); /* +inf */
|
||||||
|
CHECK(edp_bl_double_to_q16(0xfff0000000000000ull, cap, &q) == -1); /* -inf */
|
||||||
|
CHECK(edp_bl_double_to_q16(bits_of(-1.0), cap, &q) == -1 && edp_bl_double_to_q16(bits_of(-1e-300), cap, &q) == -1);
|
||||||
|
/* Every exponent and a spread of mantissas: monotonic in the value, never above the cap, rounding within half a
|
||||||
|
* step of the host's arithmetic. */
|
||||||
|
uint64_t seed = 0x9e3779b97f4a7c15ull, prev = 0;
|
||||||
|
for (uint32_t e = 0; e < 0x7ff; e++) {
|
||||||
|
for (unsigned k = 0; k < 8; k++) {
|
||||||
|
seed = seed * 6364136223846793005ull + 1442695040888963407ull;
|
||||||
|
uint64_t frac = k == 0 ? 0 : k == 1 ? (1ull << 52) - 1 : seed >> 12;
|
||||||
|
uint64_t b = (uint64_t)e << 52 | frac;
|
||||||
|
double d;
|
||||||
|
memcpy(&d, &b, sizeof d);
|
||||||
|
CHECK(edp_bl_double_to_q16(b, cap, &q) == 0 && q <= cap);
|
||||||
|
double want = d * 65536.0;
|
||||||
|
if (want < (double)cap) CHECK((double)q >= want - 0.5 && (double)q <= want + 0.5);
|
||||||
|
else CHECK(q == cap);
|
||||||
|
if (k == 0) { CHECK(q >= prev); prev = q; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void test_helpers(void)
|
static void test_helpers(void)
|
||||||
{
|
{
|
||||||
uint32_t bits = 0;
|
uint32_t bits = 0;
|
||||||
@@ -272,6 +320,7 @@ static void test_helpers(void)
|
|||||||
CHECK(edp_bl_encode(0x5A3, 1, b) == 2 && b[0] == 0x05 && b[1] == 0xA3 && edp_bl_decode(b, 1) == 0x5A3);
|
CHECK(edp_bl_encode(0x5A3, 1, b) == 2 && b[0] == 0x05 && b[1] == 0xA3 && edp_bl_decode(b, 1) == 0x5A3);
|
||||||
CHECK(edp_bl_encode(0xC8, 0, b) == 1 && b[0] == 0xC8 && edp_bl_decode(b, 0) == 0xC8);
|
CHECK(edp_bl_encode(0xC8, 0, b) == 1 && b[0] == 0xC8 && edp_bl_decode(b, 0) == 0xC8);
|
||||||
test_curve();
|
test_curve();
|
||||||
|
test_swap_double();
|
||||||
uint8_t caps[4] = {0x03, 0x07, 0x06, 0x10};
|
uint8_t caps[4] = {0x03, 0x07, 0x06, 0x10};
|
||||||
uint64_t x1 = edp_bl_reply_x1(EDP_BL_STATE_MODE, EDP_BL_DETAIL_BITS, caps, 0x12);
|
uint64_t x1 = edp_bl_reply_x1(EDP_BL_STATE_MODE, EDP_BL_DETAIL_BITS, caps, 0x12);
|
||||||
CHECK(edp_bl_reply_abi(x1) == EDP_BL_ABI && edp_bl_reply_state(x1) == EDP_BL_STATE_MODE &&
|
CHECK(edp_bl_reply_abi(x1) == EDP_BL_ABI && edp_bl_reply_state(x1) == EDP_BL_STATE_MODE &&
|
||||||
|
|||||||
@@ -58,6 +58,13 @@ MUTATIONS = (
|
|||||||
'if (nits_q16 >= full) return max - 1;'),
|
'if (nits_q16 >= full) return max - 1;'),
|
||||||
('minimum below the floor', 'compat/edp-backlight.h', 'return configured > at_floor ? configured : at_floor;',
|
('minimum below the floor', 'compat/edp-backlight.h', 'return configured > at_floor ? configured : at_floor;',
|
||||||
'return configured + 0 * at_floor;'),
|
'return configured + 0 * at_floor;'),
|
||||||
|
('swap NaN accepted', 'compat/edp-backlight.h', 'if (exponent == 0x7ffu || (bits >> 63 && bits << 1)) return -1;',
|
||||||
|
'if (bits >> 63 && bits << 1) return -1;'),
|
||||||
|
('swap negative accepted', 'compat/edp-backlight.h',
|
||||||
|
'if (exponent == 0x7ffu || (bits >> 63 && bits << 1)) return -1;', 'if (exponent == 0x7ffu) return -1;'),
|
||||||
|
('swap nits truncated', 'compat/edp-backlight.h',
|
||||||
|
'v = (mantissa + (1ull << (1059 - exponent - 1))) >> (1059 - exponent);', 'v = mantissa >> (1059 - exponent);'),
|
||||||
|
('swap cap ignored', 'compat/edp-backlight.h', '*q16 = v > cap_q16 ? cap_q16 : v;', '*q16 = v + 0 * cap_q16;'),
|
||||||
('no clamp', 'compat/edp-backlight.c', 'uint32_t level = edp_bl_clamp(request, s->floor, s->max);',
|
('no clamp', 'compat/edp-backlight.c', 'uint32_t level = edp_bl_clamp(request, s->floor, s->max);',
|
||||||
'uint32_t level = (uint32_t)request;'),
|
'uint32_t level = (uint32_t)request;'),
|
||||||
('mask bits dropped', 'compat/edp-backlight.c', ' << 1 | (status & EDP_INTR_MASK_BITS));', ' << 1);'),
|
('mask bits dropped', 'compat/edp-backlight.c', ' << 1 | (status & EDP_INTR_MASK_BITS));', ' << 1);'),
|
||||||
|
|||||||
Reference in new issue
Block a user