Nugget
Loading...
Searching...
No Matches
gte-math.hh
Go to the documentation of this file.
1/*
2
3MIT License
4
5Copyright (c) 2026 PCSX-Redux authors
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26
27#pragma once
28
29#include "psyqo/bezier.hh"
30#include "psyqo/fixed-point.hh"
31#include "psyqo/gte-kernels.hh"
33#include "psyqo/matrix.hh"
34#include "psyqo/vector.hh"
35
36/*
37 * GteMath - the SoftMath operations that the GTE can do in hardware.
38 *
39 * These mirror SoftMath's signatures and argument conventions exactly, so they
40 * are drop-in. Where SoftMath has a quirk, this keeps the quirk rather than
41 * fixing it, because a caller swapping one for the other should not have to
42 * re-derive anything. multiplyMatrix33 in particular computes out = m2 * m1,
43 * matching SoftMath.
44 *
45 * WHY THESE ARE FREE FUNCTIONS AND NOT A SCOPED, REGISTER-OWNING OBJECT.
46 * Measured on hardware (src/mips/tests/gte-math-bench), cycles per element,
47 * SCPH-5501, caller at -O2, "cold" meaning the matrix upload is paid INSIDE the
48 * call, once per element:
49 *
50 * operation soft cold hot cold speedup
51 * matrixVecMul3 271.66 126.58 47.63 2.15x
52 * matrixVecMul3xy 170.70 123.60 45.71 1.38x
53 * crossProductVec3 187.76 73.32 46.56 2.56x
54 * project 709.41 140.03 52.58 5.07x
55 * multiplyMatrix33 1144.06 209.38 127.63 5.46x
56 *
57 * There is no crossover: the GTE wins at a batch of one for everything
58 * measured, so the simple form never loses and needs no caveats. Hoisting the
59 * setup is still worth about 2.7x on top, which is what the batching helpers
60 * further down are for - but that is an optimisation, not the architecture.
61 *
62 * The reason the software side is so expensive is not instruction count. On the
63 * R3000A `mult` and `divu` are multi-cycle and `mflo` interlocks, so every
64 * FixedPoint multiply stalls. Three independent operations above work out at
65 * 28-31 cycles per fixed-point multiply. `project` is dominated by a single
66 * `divu`: 681 of its 709 cycles are the one divide.
67 *
68 * INLINING. psyqo builds at -Os, where GCC does NOT inline the GTE register
69 * accessors - it emits `jal` into writeSafe/read, and that measured up to +67%
70 * on the cold numbers above. Everything here is therefore in the header and
71 * marked always_inline. Do not move these to a .cpp built at -Os.
72 *
73 * REGISTER CLOBBERS. Each function documents what it destroys. This is the real
74 * API surface: a caller who has a rotation matrix loaded for projection cannot
75 * casually call crossProductVec3 in the middle of a render loop, because the
76 * GTE's cross product uses the rotation matrix DIAGONAL as one of its operands.
77 * That one is a landmine, so it is spelled out on every function rather than
78 * once at the top.
79 *
80 * RESTORING. Prefer re-uploading from the copy you still have in RAM over
81 * reading registers back. A Matrix33 is five ctc2; reading it back is nine
82 * mfc2 plus hazard nops. "Save and restore" is the expensive direction here.
83 */
84
85namespace psyqo {
86
87namespace GteMath {
88
89#define PSYQO_GTE_MATH_INLINE [[gnu::always_inline]] static inline
90
100 GTE::writeUnsafe<GTE::PseudoRegister::Rotation>(m);
101 GTE::writeSafe<GTE::PseudoRegister::V0>(v);
102 GTE::Kernels::mvmva<GTE::Kernels::MX::RT, GTE::Kernels::MV::V0>();
103 *out = Vec3(GTE::readSafe<GTE::PseudoRegister::SV>());
104}
105
112 GTE::writeUnsafe<GTE::PseudoRegister::Rotation>(m);
113 GTE::writeSafe<GTE::PseudoRegister::V0>(v);
114 GTE::Kernels::mvmva<GTE::Kernels::MX::RT, GTE::Kernels::MV::V0>();
115 Vec3 r = Vec3(GTE::readSafe<GTE::PseudoRegister::SV>());
116 out->x = r.x;
117 out->y = r.y;
118}
119
125PSYQO_GTE_MATH_INLINE FixedPoint<> matrixVecMul3z(const Matrix33 &m, const Vec3 &v) {
126 GTE::writeUnsafe<GTE::PseudoRegister::Rotation>(m);
127 GTE::writeSafe<GTE::PseudoRegister::V0>(v);
128 GTE::Kernels::mvmva<GTE::Kernels::MX::RT, GTE::Kernels::MV::V0>();
129 return Vec3(GTE::readSafe<GTE::PseudoRegister::SV>()).z;
130}
131
142 return {{
143 {c0.x, c1.x, c2.x},
144 {c0.y, c1.y, c2.y},
145 {c0.z, c1.z, c2.z},
146 }};
147}
148
162 GTE::writeUnsafe<GTE::PseudoRegister::Rotation>(m2);
163 const Vec3 cols[3] = {
164 {m1.vs[0].x, m1.vs[1].x, m1.vs[2].x},
165 {m1.vs[0].y, m1.vs[1].y, m1.vs[2].y},
166 {m1.vs[0].z, m1.vs[1].z, m1.vs[2].z},
167 };
168 Vec3 res[3];
169 for (unsigned j = 0; j < 3; j++) {
170 GTE::writeSafe<GTE::PseudoRegister::V0>(cols[j]);
171 GTE::Kernels::mvmva<GTE::Kernels::MX::RT, GTE::Kernels::MV::V0>();
172 res[j] = Vec3(GTE::readSafe<GTE::PseudoRegister::SV>());
173 }
174 out->vs[0] = {res[0].x, res[1].x, res[2].x};
175 out->vs[1] = {res[0].y, res[1].y, res[2].y};
176 out->vs[2] = {res[0].z, res[1].z, res[2].z};
177}
178
192 Vec3 a = v1;
193 Vec3 b = v2;
194 GTE::write<GTE::Register::R11R12, GTE::Unsafe>(a.x.raw());
195 GTE::write<GTE::Register::R22R23, GTE::Unsafe>(a.y.raw());
196 GTE::write<GTE::Register::R33, GTE::Unsafe>(a.z.raw());
197 GTE::write<GTE::Register::IR1, GTE::Unsafe>(reinterpret_cast<uint32_t *>(&b.x));
198 GTE::write<GTE::Register::IR2, GTE::Unsafe>(reinterpret_cast<uint32_t *>(&b.y));
199 GTE::write<GTE::Register::IR3, GTE::Safe>(reinterpret_cast<uint32_t *>(&b.z));
200 GTE::Kernels::cp();
201 GTE::read<GTE::PseudoRegister::LV>(*out);
202}
203
222PSYQO_GTE_MATH_INLINE FixedPoint<> inverseSquareRootSeed(FixedPoint<> x) {
223 GTE::write<GTE::Register::LZCS, GTE::Safe>(x.raw());
224 int32_t shift = (5 + int32_t(GTE::readRaw<GTE::Register::LZCR>())) / 2;
225 return FixedPoint<>(int32_t(1) << shift, FixedPoint<>::RAW);
226}
227
244void normalizeVec3(Vec3 *v);
245
257
277PSYQO_GTE_MATH_INLINE Vec3 cubic(const Vec3 &a, const Vec3 &b, const Vec3 &c, const Vec3 &d, FixedPoint<> t) {
278 using namespace psyqo::fixed_point_literals;
279 FixedPoint<> mt = 1.0_fp - t;
280 FixedPoint<> mt2 = mt * mt;
281 FixedPoint<> t2 = t * t;
282 FixedPoint<> f4 = t2 * t;
283 GTE::writeUnsafe<GTE::PseudoRegister::Rotation>(fromColumns(a, b, c));
284 GTE::writeUnsafe<GTE::PseudoRegister::Translation>(Vec3{d.x * f4, d.y * f4, d.z * f4});
285 GTE::writeSafe<GTE::PseudoRegister::V0>(Vec3{mt2 * mt, mt2 * t * 3, mt * t2 * 3});
286 GTE::Kernels::mvmva<GTE::Kernels::MX::RT, GTE::Kernels::MV::V0, GTE::Kernels::TV::TR>();
287 return Vec3(GTE::readSafe<GTE::PseudoRegister::SV>());
288}
289
303PSYQO_GTE_MATH_INLINE Vec3 cubicDerivative(const Vec3 &a, const Vec3 &b, const Vec3 &c, const Vec3 &d,
304 FixedPoint<> t) {
305 using namespace psyqo::fixed_point_literals;
306 FixedPoint<> mt = 1.0_fp - t;
307 Vec3 ab = {b.x - a.x, b.y - a.y, b.z - a.z};
308 Vec3 bc = {c.x - b.x, c.y - b.y, c.z - b.z};
309 Vec3 cd = {d.x - c.x, d.y - c.y, d.z - c.z};
310 GTE::writeUnsafe<GTE::PseudoRegister::Rotation>(fromColumns(ab, bc, cd));
311 GTE::writeSafe<GTE::PseudoRegister::V0>(Vec3{mt * mt, mt * t * 2, t * t});
312 GTE::Kernels::mvmva<GTE::Kernels::MX::RT, GTE::Kernels::MV::V0>();
313 return Vec3(GTE::readSafe<GTE::PseudoRegister::SV>());
314}
315
316#undef PSYQO_GTE_MATH_INLINE
317
318} // namespace GteMath
319
320} // namespace psyqo
uint32_t t
Definition cop0.c:79
uint32_t r
Definition cpu.c:222
uint32_t out
Definition cpu.c:62
uint8_t cd
Definition gte-depthcue.c:206
uint8_t b
Definition gte-depthcue.c:39
#define PSYQO_GTE_MATH_INLINE
Definition gte-math.hh:89
volatile void * c1
Definition load-timings.c:505
volatile void * c0
Definition load-timings.c:504
PSYQO_GTE_MATH_INLINE void fastNormalizeVec3(Vec3 *v)
Normalises a vector in place, without the exact square root.
Definition gte-math.hh:256
void normalizeVec3(Vec3 *v)
Normalises a vector in place.
Definition gte-math.cpp:38
PSYQO_GTE_MATH_INLINE Vec3 cubic(const Vec3 &a, const Vec3 &b, const Vec3 &c, const Vec3 &d, FixedPoint<> t)
Evaluates a cubic Bezier at t.
Definition gte-math.hh:277
PSYQO_GTE_MATH_INLINE void multiplyMatrix33(const Matrix33 &m1, const Matrix33 &m2, Matrix33 *out)
Multiplies two matrices.
Definition gte-math.hh:161
PSYQO_GTE_MATH_INLINE FixedPoint matrixVecMul3z(const Matrix33 &m, const Vec3 &v)
Multiplies a vector by a matrix, keeping only z.
Definition gte-math.hh:125
PSYQO_GTE_MATH_INLINE Matrix33 fromColumns(const Vec3 &c0, const Vec3 &c1, const Vec3 &c2)
Builds a matrix from three column vectors.
Definition gte-math.hh:141
PSYQO_GTE_MATH_INLINE void matrixVecMul3xy(const Matrix33 &m, const Vec3 &v, Vec2 *out)
Multiplies a vector by a matrix, keeping only x and y.
Definition gte-math.hh:111
PSYQO_GTE_MATH_INLINE Vec3 cubicDerivative(const Vec3 &a, const Vec3 &b, const Vec3 &c, const Vec3 &d, FixedPoint<> t)
Evaluates the derivative of a cubic Bezier at t.
Definition gte-math.hh:303
PSYQO_GTE_MATH_INLINE void matrixVecMul3(const Matrix33 &m, const Vec3 &v, Vec3 *out)
Multiplies a vector by a matrix.
Definition gte-math.hh:99
PSYQO_GTE_MATH_INLINE FixedPoint inverseSquareRootSeed(FixedPoint<> x)
The seed for an inverse square root, from the GTE's leading-bit count.
Definition gte-math.hh:222
PSYQO_GTE_MATH_INLINE void crossProductVec3(const Vec3 &v1, const Vec3 &v2, Vec3 *out)
Cross product of two vectors.
Definition gte-math.hh:191
Definition lua.hh:38
Vector< 3 > Vec3
Definition vector.hh:242
Definition matrix.hh:33
Vec3 vs[3]
Definition matrix.hh:34
FixedPoint< precisionBits, T > x
Definition vector.hh:49
PSYQO_NO_UNIQUE_ADDR std::conditional_t<(N > 2), FixedPoint< precisionBits, T >, EmptyZ > z
Definition vector.hh:51
FixedPoint< precisionBits, T > y
Definition vector.hh:49
static int c
Definition syscalls.h:122
void uint32_t(classId, spec)
uint16_t v2
Definition timers.c:262
uint16_t v1
Definition timers.c:260