Profile and optimize retained rendering

This commit is contained in:
gamer147
2026-07-22 14:19:20 -04:00
parent 1625b3bf54
commit 838f6d4245
17 changed files with 1619 additions and 144 deletions

View File

@@ -40,46 +40,74 @@ public static class Transform2DMath
{
public static Affine2D Build(TransformState t, RotationCycleState cycle = default)
{
double[] m = Identity();
Matrix3D m = Identity();
m = Mul(m, Translation(-t.AnchorX, -t.AnchorY, -t.AnchorZ));
m = Mul(m, Scale(t.ScaleX, t.ScaleY, t.ScaleZ));
m = Mul(m, AxisAngle(t.RotationAxisX, t.RotationAxisY, t.RotationAxisZ, t.RotationAngleDegrees));
m = Mul(m, Translation(t.TranslateX, t.TranslateY, t.TranslateZ));
if (cycle.Enabled) m = Mul(m, AxisAngle(cycle.AxisX, cycle.AxisY, cycle.AxisZ, cycle.AngleDegrees));
m = Mul(m, Translation(t.AnchorX, t.AnchorY, t.AnchorZ));
return new(m[0], m[1], m[4], m[5], m[12], m[13]);
return new(m.M11, m.M12, m.M21, m.M22, m.TX, m.TY);
}
public static (double X, double Y) Apply(double x, double y, TransformState transform,
RotationCycleState cycle = default)
=> Build(transform, cycle).Apply(x, y);
private static double[] Identity() => new double[] { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
private static double[] Scale(double x, double y, double z)
=> new double[] { x,0,0,0, 0,y,0,0, 0,0,z,0, 0,0,0,1 };
private static double[] Translation(double x, double y, double z)
=> new double[] { 1,0,0,0, 0,1,0,0, 0,0,1,0, x,y,z,1 };
// AGE composes affine 4x4 row-vector matrices, whose last column is always (0,0,0,1). Carry only
// the 3x3 linear part and translation row as a value type: the old double[16] implementation allocated
// about eleven arrays per rendered object, or roughly 2.2 MB on every DEBUGMAP composition.
private readonly record struct Matrix3D(
double M11, double M12, double M13,
double M21, double M22, double M23,
double M31, double M32, double M33,
double TX, double TY, double TZ);
private static double[] AxisAngle(double x, double y, double z, double degrees)
private static Matrix3D Identity() => new(
1,0,0, 0,1,0, 0,0,1, 0,0,0);
private static Matrix3D Scale(double x, double y, double z) => new(
x,0,0, 0,y,0, 0,0,z, 0,0,0);
private static Matrix3D Translation(double x, double y, double z) => new(
1,0,0, 0,1,0, 0,0,1, x,y,z);
private static Matrix3D AxisAngle(double x, double y, double z, double degrees)
{
double len = System.Math.Sqrt(x*x + y*y + z*z);
if (len < 1e-12 || System.Math.Abs(degrees) < 1e-12) return Identity();
x /= len; y /= len; z /= len;
double r = degrees * System.Math.PI / 180.0, c = System.Math.Cos(r), s = System.Math.Sin(r), q = 1-c;
return new double[] {
x*x*q+c, x*y*q+z*s, x*z*q-y*s, 0,
x*y*q-z*s, y*y*q+c, y*z*q+x*s, 0,
x*z*q+y*s, y*z*q-x*s, z*z*q+c, 0,
0,0,0,1
};
return new(
x*x*q+c, x*y*q+z*s, x*z*q-y*s,
x*y*q-z*s, y*y*q+c, y*z*q+x*s,
x*z*q+y*s, y*z*q-x*s, z*z*q+c,
0,0,0);
}
private static double[] Mul(double[] a, double[] b)
private static Matrix3D Mul(Matrix3D a, Matrix3D b) => new(
Sum4(a.M11*b.M11, a.M12*b.M21, a.M13*b.M31, 0),
Sum4(a.M11*b.M12, a.M12*b.M22, a.M13*b.M32, 0),
Sum4(a.M11*b.M13, a.M12*b.M23, a.M13*b.M33, 0),
Sum4(a.M21*b.M11, a.M22*b.M21, a.M23*b.M31, 0),
Sum4(a.M21*b.M12, a.M22*b.M22, a.M23*b.M32, 0),
Sum4(a.M21*b.M13, a.M22*b.M23, a.M23*b.M33, 0),
Sum4(a.M31*b.M11, a.M32*b.M21, a.M33*b.M31, 0),
Sum4(a.M31*b.M12, a.M32*b.M22, a.M33*b.M32, 0),
Sum4(a.M31*b.M13, a.M32*b.M23, a.M33*b.M33, 0),
Sum4(a.TX*b.M11, a.TY*b.M21, a.TZ*b.M31, b.TX),
Sum4(a.TX*b.M12, a.TY*b.M22, a.TZ*b.M32, b.TY),
Sum4(a.TX*b.M13, a.TY*b.M23, a.TZ*b.M33, b.TZ));
// Accumulate in the same order as the former 4x4 loop so boundary-sensitive nearest-neighbour
// projection retains its floating-point behavior while avoiding an intermediate array.
private static double Sum4(double a, double b, double c, double d)
{
var o = new double[16];
for (int row=0; row<4; row++)
for (int col=0; col<4; col++)
for (int k=0; k<4; k++) o[row*4+col] += a[row*4+k] * b[k*4+col];
return o;
double result = 0;
result += a;
result += b;
result += c;
result += d;
return result;
}
}