Files
ppsspp/GPU/Software/GEMath.cpp
T
Henrik RydgårdandClaude Opus 5.5 32c41a3417 SoftGPU: Gather the GE arithmetic in GEMath.h
The float24 truncations, reciprocal and rsqrt tables, GE adder and row sums,
the setup reciprocal for planes, the mip log2, texture coordinate precision,
the lighting pow and color scaling, and the antialiased line alpha, from
TransformUnit, Rasterizer and Lighting, so they can be unit tested. No
behavior change.

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

262 lines
16 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 "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 },
};
// 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
return copysign(ldexpf((float)q, -16 - e), w);
}
// 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 ldexpf((float)q, -16 - (e >> 1));
}
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).
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 }, { 7279552, -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 }, { 5948799, -129 }, { 5932287, -129 }, { 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 }, { 5149824, -96 }, { 5137504, -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 }, { 4219008, -64 }, { 4210752, -64 }, { 4202496, -64 },
};
int64_t GESetupRecip(uint64_t absDet, int *e) {
int ex = 63;
while (!(absDet >> ex))
--ex;
*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 floored rather than a rounded float multiply
// (gpu/probe exp102: e = 1.1).
int32_t ix;
memcpy(&ix, &v, sizeof(ix));
double t = std::floor((double)e * (double)(ix - 0x3F800000)) + 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;
if (std::abs(x1 - x0) < std::abs(y1 - y0)) {
std::swap(x0, y0);
std::swap(x1, y1);
std::swap(PX, PY);
}
int64_t den = x1 - x0;
if (den == 0)
return 128;
// o = 16 * (offset in subpixels) = num / den.
int64_t num = 16 * ((PY - y0) * den - (y1 - y0) * (PX - x0));
if (den < 0) {
num = -num;
den = -den;
}
const int64_t ceilQ = num >= 0 ? (num + den - 1) / den : -((-num) / den);
const int64_t v = ceilQ - 1;
return (int)std::clamp<int64_t>(128 - (v < 0 ? -v : v), 0, 255);
}