Skip to content

Commit 96c979c

Browse files
javachefacebook-github-bot
authored andcommitted
Reduce MatrixMathHelper allocations
Summary: Matrix decomposition runs while applying view transforms and currently allocates a nested normalized matrix plus new arrays for each vector operation. Flatten the normalized matrix and update the temporary rows in place to remove ten allocations per decomposition without changing the public helpers. Changelog: [Internal] Differential Revision: D118277120
1 parent b79264e commit 96c979c

2 files changed

Lines changed: 87 additions & 32 deletions

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/MatrixMathHelper.kt

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
package com.facebook.react.uimanager
99

1010
import com.facebook.infer.annotation.Assertions
11-
import kotlin.math.abs
1211
import kotlin.math.atan2
1312
import kotlin.math.cos
1413
import kotlin.math.sin
@@ -22,11 +21,7 @@ import kotlin.math.tan
2221
public object MatrixMathHelper {
2322
private const val EPSILON = .00001
2423

25-
private fun isZero(d: Double): Boolean {
26-
return if (java.lang.Double.isNaN(d)) {
27-
false
28-
} else abs(d) < EPSILON
29-
}
24+
private fun isZero(d: Double): Boolean = d > -EPSILON && d < EPSILON
3025

3126
@JvmStatic
3227
public fun multiplyInto(out: DoubleArray, a: DoubleArray, b: DoubleArray) {
@@ -92,17 +87,16 @@ public object MatrixMathHelper {
9287
val translation = ctx.translation
9388
val rotationDegrees = ctx.rotationDegrees
9489

95-
// create normalized, 2d array matrix
96-
// and normalized 1d array perspectiveMatrix with redefined 4th column
9790
if (isZero(transformMatrix[15])) {
9891
return
9992
}
100-
val matrix = Array(4) { DoubleArray(4) }
93+
94+
val normalizedMatrix = DoubleArray(16)
10195
val perspectiveMatrix = DoubleArray(16)
10296
for (i in 0..3) {
10397
for (j in 0..3) {
10498
val value = transformMatrix[i * 4 + j] / transformMatrix[15]
105-
matrix[i][j] = value
99+
normalizedMatrix[i * 4 + j] = value
106100
perspectiveMatrix[i * 4 + j] = if (j == 3) 0.0 else value
107101
}
108102
}
@@ -114,10 +108,19 @@ public object MatrixMathHelper {
114108
}
115109

116110
// isolate perspective
117-
if (!isZero(matrix[0][3]) || !isZero(matrix[1][3]) || !isZero(matrix[2][3])) {
111+
if (
112+
!isZero(normalizedMatrix[3]) ||
113+
!isZero(normalizedMatrix[7]) ||
114+
!isZero(normalizedMatrix[11])
115+
) {
118116
// rightHandSide is the right hand side of the equation.
119117
// rightHandSide is a vector, or point in 3d space relative to the origin.
120-
val rightHandSide = doubleArrayOf(matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3])
118+
val rightHandSide = doubleArrayOf(
119+
normalizedMatrix[3],
120+
normalizedMatrix[7],
121+
normalizedMatrix[11],
122+
normalizedMatrix[15],
123+
)
121124

122125
// Solve the equation by inverting perspectiveMatrix and multiplying
123126
// rightHandSide by the inverse.
@@ -126,50 +129,51 @@ public object MatrixMathHelper {
126129
multiplyVectorByMatrix(rightHandSide, transposedInversePerspectiveMatrix, perspective)
127130
} else {
128131
// no perspective
132+
perspective[0] = 0.0
133+
perspective[1] = 0.0
129134
perspective[2] = 0.0
130-
perspective[1] = perspective[2]
131-
perspective[0] = perspective[1]
132135
perspective[3] = 1.0
133136
}
134137

135-
// translation is simple
136-
for (i in 0..2) {
137-
translation[i] = matrix[3][i]
138-
}
138+
translation[0] = normalizedMatrix[12]
139+
translation[1] = normalizedMatrix[13]
140+
translation[2] = normalizedMatrix[14]
139141

140142
// Now get scale and shear.
141143
// 'row' is a 3 element array of 3 component vectors
142144
val row = Array(3) { DoubleArray(3) }
143145
for (i in 0..2) {
144-
row[i][0] = matrix[i][0]
145-
row[i][1] = matrix[i][1]
146-
row[i][2] = matrix[i][2]
146+
row[i][0] = normalizedMatrix[i * 4]
147+
row[i][1] = normalizedMatrix[i * 4 + 1]
148+
row[i][2] = normalizedMatrix[i * 4 + 2]
147149
}
148150

149151
// Compute X scale factor and normalize first row.
150152
scale[0] = v3Length(row[0])
151-
row[0] = v3Normalize(row[0], scale[0])
153+
v3NormalizeInPlace(row[0], scale[0])
152154

153155
// Compute XY shear factor and make 2nd row orthogonal to 1st.
154156
skew[0] = v3Dot(row[0], row[1])
155-
row[1] = v3Combine(row[1], row[0], 1.0, -skew[0])
157+
v3CombineInPlace(row[1], row[0], 1.0, -skew[0])
156158

157159
// Now, compute Y scale and normalize 2nd row.
158-
scale[1] = v3Length(row[1])
159-
row[1] = v3Normalize(row[1], scale[1])
160-
skew[0] /= scale[1]
160+
val scaleY = v3Length(row[1])
161+
scale[1] = scaleY
162+
v3NormalizeInPlace(row[1], scaleY)
163+
skew[0] /= scaleY
161164

162165
// Compute XZ and YZ shears, orthogonalize 3rd row
163166
skew[1] = v3Dot(row[0], row[2])
164-
row[2] = v3Combine(row[2], row[0], 1.0, -skew[1])
167+
v3CombineInPlace(row[2], row[0], 1.0, -skew[1])
165168
skew[2] = v3Dot(row[1], row[2])
166-
row[2] = v3Combine(row[2], row[1], 1.0, -skew[2])
169+
v3CombineInPlace(row[2], row[1], 1.0, -skew[2])
167170

168171
// Next, get Z scale and normalize 3rd row.
169-
scale[2] = v3Length(row[2])
170-
row[2] = v3Normalize(row[2], scale[2])
171-
skew[1] /= scale[2]
172-
skew[2] /= scale[2]
172+
val scaleZ = v3Length(row[2])
173+
scale[2] = scaleZ
174+
v3NormalizeInPlace(row[2], scaleZ)
175+
skew[1] /= scaleZ
176+
skew[2] /= scaleZ
173177

174178
// At this point, the matrix (in rows) is orthonormal.
175179
// Check for a coordinate system flip. If the determinant
@@ -340,6 +344,13 @@ public object MatrixMathHelper {
340344
return doubleArrayOf(vector[0] * im, vector[1] * im, vector[2] * im)
341345
}
342346

347+
private fun v3NormalizeInPlace(vector: DoubleArray, norm: Double) {
348+
val inverseMagnitude = 1.0 / norm
349+
vector[0] *= inverseMagnitude
350+
vector[1] *= inverseMagnitude
351+
vector[2] *= inverseMagnitude
352+
}
353+
343354
/**
344355
* The dot product of a and b, two 3-element vectors. From:
345356
* https://code.google.com/p/webgl-mjs/source/browse/mjs.js
@@ -367,6 +378,17 @@ public object MatrixMathHelper {
367378
)
368379
}
369380

381+
private fun v3CombineInPlace(
382+
a: DoubleArray,
383+
b: DoubleArray,
384+
aScale: Double,
385+
bScale: Double,
386+
) {
387+
a[0] = aScale * a[0] + bScale * b[0]
388+
a[1] = aScale * a[1] + bScale * b[1]
389+
a[2] = aScale * a[2] + bScale * b[2]
390+
}
391+
370392
/**
371393
* From:
372394
* http://www.opensource.apple.com/source/WebCore/WebCore-514/platform/graphics/transforms/TransformationMatrix.cpp

packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/MatrixMathHelperTest.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,39 @@ class MatrixMathHelperTest {
8383
verifyZRotatedMatrix(42.55555555555, 0.0, 0.0, 42.556)
8484
}
8585

86+
@Test
87+
fun testDecomposingTranslationAndScale() {
88+
val ctx = MatrixDecompositionContext()
89+
90+
MatrixMathHelper.decomposeMatrix(
91+
doubleArrayOf(
92+
2.0,
93+
0.0,
94+
0.0,
95+
0.0,
96+
0.0,
97+
3.0,
98+
0.0,
99+
0.0,
100+
0.0,
101+
0.0,
102+
4.0,
103+
0.0,
104+
5.0,
105+
6.0,
106+
7.0,
107+
1.0,
108+
),
109+
ctx,
110+
)
111+
112+
assertThat(ctx.perspective).containsExactly(0.0, 0.0, 0.0, 1.0)
113+
assertThat(ctx.scale).containsExactly(2.0, 3.0, 4.0)
114+
assertThat(ctx.skew).containsExactly(0.0, 0.0, 0.0)
115+
assertThat(ctx.translation).containsExactly(5.0, 6.0, 7.0)
116+
assertThat(ctx.rotationDegrees).containsExactly(0.0, 0.0, 0.0)
117+
}
118+
86119
@Test
87120
fun testDecomposing4x4MatrixToProduceAccurateYaxisAngles() {
88121
val angles = doubleArrayOf(30.0, 45.0, 60.0, 75.0, 90.0)

0 commit comments

Comments
 (0)