Files
ppsspp/GPU/Software/GEMath.cpp
T
Henrik RydgårdandClaude Opus 5.5 63a05db8d2 SoftGPU: Skin the x, y and z components together in vector lanes
Skinning did each component's weighted bone entries and its chain of
GEAdds separately in scalar code. The components now go in lanes, with
GEMulFloat24x4 for the weighted entries, one-term row sums for the
products and GEAdd4 for the sums, in the same order. Checked against the
scalar version on every vertex of 143 skinned frame dumps.

GEMulFloat24x4 matches ProductToFloat24, so inf and NaN bone entries
behave as before. GEAdd4 handles two zero operands without its fallback.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 09:07:12 +02:00

328 lines
19 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Common/BitScan.h"
#include "GPU/Software/GEMath.h"
// The GE's reciprocal: the top 7 bits of w's 15-bit float24 mantissa pick a segment, which the low
// 8 bits interpolate linearly. b is the segment's start in units of 2^-17, m its slope in units of
// 2^-23 per step, and 63 a rounding bias below half. Measured for every mantissa on hardware.
struct GERecipSegment {
int32_t b;
int32_t m;
};
static const GERecipSegment geRecipSegments[128] = {
{ 131072, -254 }, { 130055, -250 }, { 129054, -246 }, { 128071, -243 }, { 127099, -239 }, { 126143, -235 }, { 125203, -232 }, { 124274, -228 },
{ 123361, -225 }, { 122461, -222 }, { 121574, -219 }, { 120700, -216 }, { 119836, -212 }, { 118986, -209 }, { 118150, -207 }, { 117323, -204 },
{ 116508, -201 }, { 115704, -198 }, { 114913, -196 }, { 114130, -193 }, { 113358, -190 }, { 112599, -188 }, { 111847, -185 }, { 111107, -183 },
{ 110375, -180 }, { 109654, -178 }, { 108943, -176 }, { 108239, -173 }, { 107545, -171 }, { 106861, -169 }, { 106184, -167 }, { 105517, -165 },
{ 104858, -163 }, { 104206, -161 }, { 103563, -159 }, { 102927, -157 }, { 102300, -155 }, { 101680, -153 }, { 101067, -151 }, { 100463, -150 },
{ 99865, -148 }, { 99273, -146 }, { 98689, -144 }, { 98113, -143 }, { 97542, -141 }, { 96977, -139 }, { 96421, -138 }, { 95869, -136 },
{ 95326, -135 }, { 94786, -133 }, { 94255, -132 }, { 93727, -130 }, { 93207, -129 }, { 92691, -127 }, { 92182, -126 }, { 91680, -125 },
{ 91180, -123 }, { 90688, -122 }, { 90201, -121 }, { 89717, -119 }, { 89240, -118 }, { 88769, -117 }, { 88302, -116 }, { 87838, -114 },
{ 87381, -113 }, { 86928, -112 }, { 86481, -111 }, { 86037, -110 }, { 85599, -109 }, { 85164, -108 }, { 84732, -106 }, { 84307, -105 },
{ 83885, -104 }, { 83468, -103 }, { 83055, -102 }, { 82646, -101 }, { 82241, -100 }, { 81839, -99 }, { 81442, -98 }, { 81049, -97 },
{ 80659, -96 }, { 80275, -96 }, { 79892, -95 }, { 79513, -94 }, { 79138, -93 }, { 78766, -92 }, { 78398, -91 }, { 78033, -90 },
{ 77673, -90 }, { 77315, -89 }, { 76960, -88 }, { 76608, -87 }, { 76260, -86 }, { 75914, -85 }, { 75574, -85 }, { 75234, -84 },
{ 74898, -83 }, { 74564, -82 }, { 74236, -82 }, { 73908, -81 }, { 73584, -80 }, { 73264, -80 }, { 72944, -79 }, { 72628, -78 },
{ 72316, -78 }, { 72005, -77 }, { 71697, -76 }, { 71393, -76 }, { 71090, -75 }, { 70789, -74 }, { 70493, -74 }, { 70197, -73 },
{ 69906, -73 }, { 69615, -72 }, { 69327, -71 }, { 69042, -71 }, { 68759, -70 }, { 68479, -70 }, { 68200, -69 }, { 67925, -69 },
{ 67650, -68 }, { 67378, -67 }, { 67109, -67 }, { 66841, -66 }, { 66577, -66 }, { 66313, -65 }, { 66052, -65 }, { 65793, -64 },
};
// f * 2^n with the given sign bit, by adding n to f's exponent: f must be positive, and the result normal.
static inline float ScaleByPow2(float f, int n, uint32_t sign) {
uint32_t bits;
memcpy(&bits, &f, sizeof(bits));
bits = (uint32_t)((int32_t)bits + n * (1 << 23)) | sign;
memcpy(&f, &bits, sizeof(f));
return f;
}
// w must be a normal float24. Returns a float24 (q has 16 significant bits, or is 2^16).
float GERecip(float w) {
uint32_t bits;
memcpy(&bits, &w, sizeof(bits));
const uint32_t i = (bits >> 8) & 0x7FFF;
const int e = (int)((bits >> 23) & 0xFF) - 127; // |w| = 1.i * 2^e
const GERecipSegment &seg = geRecipSegments[i >> 8];
const int32_t q = (64 * seg.b + 63 + seg.m * (int32_t)(i & 255)) >> 7; // 1 / 1.i in units of 2^-16
if (e >= 126) {
// The result can be a denormal (q is at most 2^16).
return copysign(ldexpf((float)q, -16 - e), w);
}
return ScaleByPow2((float)q, -16 - e, bits & 0x80000000);
}
// The GE's reciprocal square root (gpu/probe exp69, bit exact): for d = 1.i * 2^E, segment i >> 8 of the
// table for E's parity, linearly interpolated like GERecip by the low 8 bits. Even E stores 1 / sqrt(1.i),
// odd E 1 / sqrt(2 * 1.i), both in units of 2^-16.
static const GERecipSegment geRsqrtSegments[2][128] = {
{
{ 131072, -127 }, { 130562, -125 }, { 130059, -124 }, { 129562, -122 }, { 129070, -121 }, { 128584, -120 }, { 128103, -118 }, { 127628, -117 },
{ 127158, -116 }, { 126693, -114 }, { 126233, -113 }, { 125778, -112 }, { 125328, -111 }, { 124883, -110 }, { 124443, -108 }, { 124007, -107 },
{ 123575, -106 }, { 123149, -105 }, { 122726, -104 }, { 122308, -103 }, { 121894, -102 }, { 121484, -101 }, { 121079, -100 }, { 120677, -99 },
{ 120279, -98 }, { 119886, -97 }, { 119496, -96 }, { 119110, -95 }, { 118727, -94 }, { 118349, -93 }, { 117974, -92 }, { 117602, -92 },
{ 117234, -91 }, { 116869, -90 }, { 116508, -89 }, { 116150, -88 }, { 115795, -87 }, { 115444, -87 }, { 115096, -86 }, { 114751, -85 },
{ 114409, -84 }, { 114070, -83 }, { 113734, -83 }, { 113400, -82 }, { 113070, -81 }, { 112743, -81 }, { 112419, -80 }, { 112097, -79 },
{ 111778, -79 }, { 111462, -78 }, { 111148, -77 }, { 110837, -77 }, { 110529, -76 }, { 110223, -75 }, { 109920, -75 }, { 109619, -74 },
{ 109321, -73 }, { 109025, -73 }, { 108732, -72 }, { 108441, -72 }, { 108152, -71 }, { 107865, -71 }, { 107581, -70 }, { 107299, -69 },
{ 107019, -69 }, { 106742, -68 }, { 106466, -68 }, { 106193, -67 }, { 105922, -67 }, { 105652, -66 }, { 105385, -66 }, { 105120, -65 },
{ 104857, -65 }, { 104596, -64 }, { 104337, -64 }, { 104079, -63 }, { 103824, -63 }, { 103570, -62 }, { 103319, -62 }, { 103069, -62 },
{ 102821, -61 }, { 102575, -61 }, { 102330, -60 }, { 102087, -60 }, { 101846, -59 }, { 101607, -59 }, { 101369, -59 }, { 101133, -58 },
{ 100899, -58 }, { 100666, -57 }, { 100435, -57 }, { 100205, -56 }, { 99977, -56 }, { 99751, -56 }, { 99526, -55 }, { 99303, -55 },
{ 99081, -55 }, { 98860, -54 }, { 98641, -54 }, { 98424, -54 }, { 98208, -53 }, { 97993, -53 }, { 97780, -52 }, { 97568, -52 },
{ 97357, -52 }, { 97148, -51 }, { 96940, -51 }, { 96734, -51 }, { 96529, -50 }, { 96325, -50 }, { 96122, -50 }, { 95921, -50 },
{ 95721, -49 }, { 95522, -49 }, { 95325, -49 }, { 95128, -48 }, { 94933, -48 }, { 94739, -48 }, { 94546, -47 }, { 94355, -47 },
{ 94164, -47 }, { 93975, -47 }, { 93787, -46 }, { 93600, -46 }, { 93414, -46 }, { 93229, -45 }, { 93046, -45 }, { 92863, -45 }
},
{
{ 92681, -89 }, { 92321, -88 }, { 91966, -87 }, { 91614, -86 }, { 91266, -85 }, { 90923, -84 }, { 90583, -84 }, { 90247, -83 },
{ 89914, -82 }, { 89585, -81 }, { 89260, -80 }, { 88939, -79 }, { 88620, -78 }, { 88306, -77 }, { 87994, -77 }, { 87686, -76 },
{ 87381, -75 }, { 87079, -74 }, { 86780, -73 }, { 86485, -73 }, { 86192, -72 }, { 85902, -71 }, { 85615, -70 }, { 85331, -70 },
{ 85050, -69 }, { 84772, -68 }, { 84496, -68 }, { 84223, -67 }, { 83953, -66 }, { 83685, -66 }, { 83420, -65 }, { 83157, -65 },
{ 82897, -64 }, { 82639, -63 }, { 82383, -63 }, { 82130, -62 }, { 81880, -62 }, { 81631, -61 }, { 81385, -61 }, { 81141, -60 },
{ 80899, -59 }, { 80659, -59 }, { 80422, -58 }, { 80186, -58 }, { 79953, -57 }, { 79721, -57 }, { 79492, -56 }, { 79264, -56 },
{ 79039, -55 }, { 78815, -55 }, { 78594, -54 }, { 78374, -54 }, { 78156, -54 }, { 77940, -53 }, { 77725, -53 }, { 77512, -52 },
{ 77302, -52 }, { 77092, -51 }, { 76885, -51 }, { 76679, -51 }, { 76475, -50 }, { 76272, -50 }, { 76071, -49 }, { 75872, -49 },
{ 75674, -49 }, { 75478, -48 }, { 75283, -48 }, { 75090, -47 }, { 74898, -47 }, { 74707, -47 }, { 74519, -46 }, { 74331, -46 },
{ 74145, -46 }, { 73960, -45 }, { 73777, -45 }, { 73595, -45 }, { 73415, -44 }, { 73235, -44 }, { 73057, -44 }, { 72881, -43 },
{ 72705, -43 }, { 72531, -43 }, { 72358, -42 }, { 72186, -42 }, { 72016, -42 }, { 71847, -42 }, { 71679, -41 }, { 71512, -41 },
{ 71346, -41 }, { 71181, -40 }, { 71018, -40 }, { 70856, -40 }, { 70694, -40 }, { 70534, -39 }, { 70375, -39 }, { 70217, -39 },
{ 70060, -38 }, { 69905, -38 }, { 69750, -38 }, { 69596, -38 }, { 69443, -37 }, { 69291, -37 }, { 69141, -37 }, { 68991, -37 },
{ 68842, -36 }, { 68694, -36 }, { 68547, -36 }, { 68401, -36 }, { 68256, -36 }, { 68112, -35 }, { 67969, -35 }, { 67826, -35 },
{ 67685, -35 }, { 67544, -34 }, { 67405, -34 }, { 67266, -34 }, { 67128, -34 }, { 66991, -34 }, { 66854, -33 }, { 66719, -33 },
{ 66584, -33 }, { 66450, -33 }, { 66317, -33 }, { 66185, -32 }, { 66054, -32 }, { 65923, -32 }, { 65793, -32 }, { 65664, -32 }
},
};
// d must be a positive normal float24.
float GERsqrt(float d) {
uint32_t bits;
memcpy(&bits, &d, sizeof(bits));
const uint32_t i = (bits >> 8) & 0x7FFF;
const int e = (int)((bits >> 23) & 0xFF) - 127;
const GERecipSegment &seg = geRsqrtSegments[e & 1][i >> 8];
const int32_t q = (64 * seg.b + 63 + seg.m * (int32_t)(i & 255)) >> 7;
return ScaleByPow2((float)q, -16 - (e >> 1), 0);
}
Vec4F32 GEAdd4Fallback(Vec4F32 a, Vec4F32 b, Vec4S32 lanes, Vec4F32 result) {
alignas(16) float av[4], bv[4], rv[4];
alignas(16) int lv[4];
a.Store(av);
b.Store(bv);
result.Store(rv);
lanes.Store(lv);
for (int i = 0; i < 4; ++i) {
if (lv[i])
rv[i] = GEAdd(av[i], bv[i]);
}
return Vec4F32::Load(rv);
}
Vec4F32 GEMulFloat24x4Fallback(Vec4F32 a, Vec4F32 b, Vec4S32 lanes, Vec4F32 result) {
alignas(16) float av[4], bv[4], rv[4];
alignas(16) int lv[4];
a.Store(av);
b.Store(bv);
result.Store(rv);
lanes.Store(lv);
for (int i = 0; i < 4; ++i) {
if (lv[i])
rv[i] = ProductToFloat24((double)TruncateToFloat24(av[i]) * TruncateToFloat24(bv[i]));
}
return Vec4F32::Load(rv);
}
float GEAddFloat24(float a, float b) {
return TruncateToFloat24(GEAdd(a, b));
}
// A dot product as the GE's dot product unit sums it (like a matrix row without translation).
Vec4F32 GERowSum4Fallback(Vec4F32 a, const Vec4F32 b[4], int count, Vec4S32 lanes, Vec4F32 result) {
alignas(16) float av[4], bv[4][4], rv[4];
alignas(16) int lv[4];
a.Store(av);
for (int k = 0; k < 4; ++k)
av[k] = TruncateToFloat24(av[k]);
for (int k = 0; k < count; ++k)
Vec4F32(b[k]).Store(bv[k]);
result.Store(rv);
lanes.Store(lv);
for (int i = 0; i < 4; ++i) {
if (!lv[i])
continue;
GERowTerm terms[4];
for (int k = 0; k < count; ++k)
terms[k] = GEProduct(av[k], bv[k][i]);
rv[i] = GERowSum(terms, count);
}
return Vec4F32::Load(rv);
}
float GEDot(const Vec3f &a, const Vec3f &b) {
GERowTerm terms[3] = {
GEProduct(TruncateToFloat24(a.x), TruncateToFloat24(b.x)),
GEProduct(TruncateToFloat24(a.y), TruncateToFloat24(b.y)),
GEProduct(TruncateToFloat24(a.z), TruncateToFloat24(b.z)),
};
return GERowSum(terms, 3);
}
// Normalizes like the GE (gpu/probe exp69, exp61): the squared length summed like a dot product, then
// each component times GERsqrt of it, as a float24. Returns the length, d2 * GERsqrt(d2) (0 leaves v alone).
float GENormalize(Vec3f &v) {
const float d2 = GEDot(v, v);
if (!(d2 > 0.0f) || !std::isfinite(d2))
return 0.0f;
const float r = GERsqrt(d2);
for (int i = 0; i < 3; ++i)
v[i] = ProductToFloat24((double)TruncateToFloat24(v[i]) * r);
return ProductToFloat24((double)d2 * r);
}
// The GE's reciprocal for triangle setup (gpu/probe exp38-41): a 16-bit significand index, 256 segments,
// each linear over its low 8 bits, q = (2K + M * x) >> 8. Not the same table as the one for z/w.
struct GESetupRecipSegment {
int32_t k;
int32_t m;
};
static const GESetupRecipSegment geSetupRecip[256] = {
{ 8388735, -257 }, { 8355952, -254 }, { 8323568, -252 }, { 8291440, -250 },
{ 8259552, -248 }, { 8227904, -246 }, { 8196496, -244 }, { 8165328, -242 },
{ 8134400, -240 }, { 8103696, -238 }, { 8073232, -237 }, { 8043008, -235 },
{ 8012992, -233 }, { 7983200, -231 }, { 7953632, -230 }, { 7924288, -228 },
{ 7895152, -226 }, { 7866240, -225 }, { 7837520, -223 }, { 7809024, -221 },
{ 7780736, -220 }, { 7752640, -218 }, { 7724752, -217 }, { 7697072, -215 },
{ 7669584, -213 }, { 7642288, -212 }, { 7615184, -210 }, { 7588272, -209 },
{ 7561552, -208 }, { 7535024, -206 }, { 7508672, -205 }, { 7482512, -203 },
{ 7456528, -202 }, { 7430736, -200 }, { 7405104, -199 }, { 7379664, -198 },
{ 7354384, -196 }, { 7329280, -195 }, { 7304352, -194 }, { 7279600, -192 },
{ 7255008, -191 }, { 7230576, -190 }, { 7206320, -188 }, { 7182208, -187 },
{ 7158272, -186 }, { 7134496, -185 }, { 7110864, -183 }, { 7087392, -182 },
{ 7064080, -181 }, { 7040928, -180 }, { 7017920, -179 }, { 6995056, -178 },
{ 6972336, -176 }, { 6949776, -175 }, { 6927360, -174 }, { 6905088, -173 },
{ 6882960, -172 }, { 6860960, -171 }, { 6839104, -170 }, { 6817408, -169 },
{ 6795824, -168 }, { 6774384, -166 }, { 6753088, -165 }, { 6731920, -164 },
{ 6710880, -163 }, { 6689968, -162 }, { 6669200, -161 }, { 6648544, -160 },
{ 6628032, -159 }, { 6607632, -158 }, { 6587360, -157 }, { 6567216, -156 },
{ 6547200, -155 }, { 6527296, -154 }, { 6507520, -154 }, { 6487856, -153 },
{ 6468320, -152 }, { 6448896, -151 }, { 6429584, -150 }, { 6410384, -149 },
{ 6391312, -148 }, { 6372352, -147 }, { 6353488, -146 }, { 6334752, -145 },
{ 6316128, -145 }, { 6297600, -144 }, { 6279184, -143 }, { 6260880, -142 },
{ 6242672, -141 }, { 6224576, -140 }, { 6206592, -140 }, { 6188704, -139 },
{ 6170928, -138 }, { 6153248, -137 }, { 6135664, -136 }, { 6118176, -136 },
{ 6100800, -135 }, { 6083520, -134 }, { 6066336, -133 }, { 6049248, -133 },
{ 6032256, -132 }, { 6015360, -131 }, { 5998544, -130 }, { 5981840, -130 },
{ 5965232, -129 }, { 5948704, -128 }, { 5932272, -128 }, { 5915920, -127 },
{ 5899680, -126 }, { 5883504, -125 }, { 5867440, -125 }, { 5851440, -124 },
{ 5835552, -123 }, { 5819728, -123 }, { 5804000, -122 }, { 5788352, -121 },
{ 5772800, -121 }, { 5757328, -120 }, { 5741920, -119 }, { 5726608, -119 },
{ 5711392, -118 }, { 5696240, -118 }, { 5681168, -117 }, { 5666176, -116 },
{ 5651264, -116 }, { 5636432, -115 }, { 5621680, -114 }, { 5606992, -114 },
{ 5592400, -113 }, { 5577872, -113 }, { 5563424, -112 }, { 5549040, -112 },
{ 5534736, -111 }, { 5520512, -110 }, { 5506368, -110 }, { 5492272, -109 },
{ 5478272, -109 }, { 5464320, -108 }, { 5450464, -108 }, { 5436656, -107 },
{ 5422928, -106 }, { 5409264, -106 }, { 5395680, -105 }, { 5382160, -105 },
{ 5368704, -104 }, { 5355312, -104 }, { 5341984, -103 }, { 5328736, -103 },
{ 5315552, -102 }, { 5302416, -102 }, { 5289360, -101 }, { 5276368, -101 },
{ 5263440, -100 }, { 5250560, -100 }, { 5237760, -99 }, { 5225008, -99 },
{ 5212336, -98 }, { 5199712, -98 }, { 5187152, -97 }, { 5174656, -97 },
{ 5162208, -96 }, { 5149840, -96 }, { 5137520, -96 }, { 5125248, -95 },
{ 5113056, -95 }, { 5100896, -94 }, { 5088816, -94 }, { 5076784, -93 },
{ 5064816, -93 }, { 5052896, -92 }, { 5041040, -92 }, { 5029232, -92 },
{ 5017472, -91 }, { 5005776, -91 }, { 4994144, -90 }, { 4982560, -90 },
{ 4971024, -89 }, { 4959536, -89 }, { 4948112, -89 }, { 4936736, -88 },
{ 4925408, -88 }, { 4914144, -87 }, { 4902928, -87 }, { 4891760, -87 },
{ 4880640, -86 }, { 4869568, -86 }, { 4858560, -85 }, { 4847584, -85 },
{ 4836672, -85 }, { 4825792, -84 }, { 4814976, -84 }, { 4804208, -83 },
{ 4793488, -83 }, { 4782800, -83 }, { 4772176, -82 }, { 4761600, -82 },
{ 4751056, -82 }, { 4740576, -81 }, { 4730128, -81 }, { 4719744, -81 },
{ 4709392, -80 }, { 4699088, -80 }, { 4688816, -79 }, { 4678608, -79 },
{ 4668432, -79 }, { 4658304, -78 }, { 4648224, -78 }, { 4638192, -78 },
{ 4628192, -77 }, { 4618240, -77 }, { 4608320, -77 }, { 4598464, -76 },
{ 4588640, -76 }, { 4578848, -76 }, { 4569104, -75 }, { 4559408, -75 },
{ 4549744, -75 }, { 4540128, -74 }, { 4530544, -74 }, { 4521008, -74 },
{ 4511520, -74 }, { 4502048, -73 }, { 4492640, -73 }, { 4483264, -73 },
{ 4473920, -72 }, { 4464608, -72 }, { 4455360, -72 }, { 4446128, -71 },
{ 4436944, -71 }, { 4427792, -71 }, { 4418688, -71 }, { 4409616, -70 },
{ 4400576, -70 }, { 4391568, -70 }, { 4382608, -69 }, { 4373680, -69 },
{ 4364800, -69 }, { 4355936, -69 }, { 4347120, -68 }, { 4338336, -68 },
{ 4329600, -68 }, { 4320880, -67 }, { 4312208, -67 }, { 4303568, -67 },
{ 4294960, -67 }, { 4286384, -66 }, { 4277840, -66 }, { 4269344, -66 },
{ 4260880, -66 }, { 4252432, -65 }, { 4244032, -65 }, { 4235664, -65 },
{ 4227328, -65 }, { 4219024, -64 }, { 4210752, -64 }, { 4202512, -64 },
};
// Every K but segment 0's is floor(2^39 / (65536 + 256 i) / 16) * 16, the start of segment i in 1/8ths. The table
// was fit to probe data; segments it left loose (39, 161, 162, 253, 255) use that formula, which games confirmed
// for 39 and 161, and large-w UV planes (gpu/probe exp136, exp138) for 105 and 106.
int64_t GESetupRecip(uint64_t absDet, int *e) {
const uint32_t hi = (uint32_t)(absDet >> 32);
const int ex = hi ? 63 - (int)clz32_nonzero(hi) : 31 - (int)clz32_nonzero((uint32_t)absDet);
*e = ex;
const int index = (int)((absDet << 16) >> ex) - 65536;
const GESetupRecipSegment &seg = geSetupRecip[index >> 8];
return (2 * (int64_t)seg.k + seg.m * (index & 255)) >> 8;
}
float GELightPow(float v, float e) {
if (e <= 0.0f) {
return 1.0f;
}
if (v > 0.0f) {
// PSPLightPow, but the product is exact, and keeps 19 fraction bits of the log (units of 16 in
// the float's bits), truncated toward zero (gpu/probe exp102: e = 1.1 and 0.3, exp4: e = 0.05).
int32_t ix;
memcpy(&ix, &v, sizeof(ix));
double t = std::trunc((double)e * (double)(ix - 0x3F800000) * (1.0 / 16.0)) * 16.0 + 1065353216.0;
t = t >= 0.0 ? (t < 2139095039.0 ? t : 2139095039.0) : 0.0;
int32_t iy = (int32_t)t;
float y;
memcpy(&y, &iy, sizeof(y));
return y;
}
// Negative stays negative, so let's just return the original.
return v;
}
int GELineCoverageAlpha(int64_t x0, int64_t y0, int64_t x1, int64_t y1, int px, int py) {
int64_t PX = (int64_t)px * 16 + 16 / 2;
int64_t PY = (int64_t)py * 16 + 16 / 2;
// A diagonal line is y-major (gpu/probe exp137).
if (std::abs(x1 - x0) <= std::abs(y1 - y0)) {
std::swap(x0, y0);
std::swap(x1, y1);
std::swap(PX, PY);
}
const int64_t den = x1 - x0;
if (den == 0)
return 128;
// The minor offset at the pixel walks from v0 along the major axis with a slope like a triangle plane's
// gradient: the setup reciprocal of the major length, 14 fraction bits per subpixel, floored
// (gpu/probe exp112, exact).
int e;
const int64_t q = GESetupRecip((uint64_t)std::abs(den), &e);
const int64_t walk = den > 0 ? PX - x0 : x0 - PX;
const int64_t slope = (-16 * (y1 - y0) * q) >> (e + 2);
const int64_t o = ((16 * (PY - y0)) * (1 << 14) + slope * walk) >> 14;
return (int)std::clamp<int64_t>(128 - (o < 0 ? -o : o), 0, 255);
}