Reduce small memory allocations when loading ply files (#147)

* Replace for-of loops to reduce memory allocations in hot-path for ply loading

* Use temporary THREE.Quaternion and THREE.Vector instances to avoid allocations
This commit is contained in:
Noeri Huisman
2025-08-01 16:09:08 +02:00
committed by GitHub
parent 3f1d024980
commit 2f42e2e29c
2 changed files with 22 additions and 13 deletions
+8 -8
View File
@@ -239,8 +239,8 @@ export class PlyReader {
// Parse all the items in the element // Parse all the items in the element
const callback = elementCallback(element) ?? (() => {}); const callback = elementCallback(element) ?? (() => {});
for (let index = 0; index < count; index++) { for (let index = 0; index < count; index++) {
for (const parser of parsers) { for (let parserIndex = 0; parserIndex < parsers.length; parserIndex++) {
parser(); parsers[parserIndex]();
} }
callback(index, item); callback(index, item);
} }
@@ -296,17 +296,17 @@ export class PlyReader {
if (!sh1) { if (!sh1) {
throw new Error("Missing sh1"); throw new Error("Missing sh1");
} }
for (const [i, key] of sh1Props.entries()) { for (let i = 0; i < sh1Props.length; i++) {
sh1[i] = ((item[key] as number) * 8) / 255 - 4; sh1[i] = ((item[sh1Props[i]] as number) * 8) / 255 - 4;
} }
if (sh2) { if (sh2) {
for (const [i, key] of sh2Props.entries()) { for (let i = 0; i < sh2Props.length; i++) {
sh2[i] = ((item[key] as number) * 8) / 255 - 4; sh2[i] = ((item[sh2Props[i]] as number) * 8) / 255 - 4;
} }
} }
if (sh3) { if (sh3) {
for (const [i, key] of sh3Props.entries()) { for (let i = 0; i < sh3Props.length; i++) {
sh3[i] = ((item[key] as number) * 8) / 255 - 4; sh3[i] = ((item[sh3Props[i]] as number) * 8) / 255 - 4;
} }
} }
shCallback?.(index, sh1, sh2, sh3); shCallback?.(index, sh1, sh2, sh3);
+14 -5
View File
@@ -499,6 +499,10 @@ export function setPackedSplatScales(
(packedSplats[i4 + 3] & 0xff000000); (packedSplats[i4 + 3] & 0xff000000);
} }
// Temporary storage used in `encodeQuatOCtXy88R8` and `decodeQuatOctXy88R8` to
// avoid allocation new Quaternions and Vector3 instances.
const tempQuaternion = new THREE.Quaternion();
// Encode the rotation quatX, quatY, quatZ, quatW in the packedSplats Uint32Array, // Encode the rotation quatX, quatY, quatZ, quatW in the packedSplats Uint32Array,
// leaving all other fields as is. // leaving all other fields as is.
export function setPackedSplatQuat( export function setPackedSplatQuat(
@@ -510,7 +514,7 @@ export function setPackedSplatQuat(
quatW: number, quatW: number,
) { ) {
const uQuat = encodeQuatOctXy88R8( const uQuat = encodeQuatOctXy88R8(
new THREE.Quaternion(quatX, quatY, quatZ, quatW), tempQuaternion.set(quatX, quatY, quatZ, quatW),
); );
// const uQuat = encodeQuatXyz888(new THREE.Quaternion(quatX, quatY, quatZ, quatW)); // const uQuat = encodeQuatXyz888(new THREE.Quaternion(quatX, quatY, quatZ, quatW));
// const uQuat = encodeQuatEulerXyz888(new THREE.Quaternion(quatX, quatY, quatZ, quatW)); // const uQuat = encodeQuatEulerXyz888(new THREE.Quaternion(quatX, quatY, quatZ, quatW));
@@ -960,6 +964,11 @@ export function decodeQuatXyz888(
return out; return out;
} }
// Temporary storage used in `encodeQuatOCtXy88R8` and `decodeQuatOctXy88R8` to
// avoid allocation new Quaternions and Vector3 instances.
const tempNormalizedQuaternion = new THREE.Quaternion();
const tempAxis = new THREE.Vector3();
/** /**
* Encodes a THREE.Quaternion into a 24‐bit integer. * Encodes a THREE.Quaternion into a 24‐bit integer.
* *
@@ -972,7 +981,7 @@ export function decodeQuatXyz888(
*/ */
export function encodeQuatOctXy88R8(q: THREE.Quaternion): number { export function encodeQuatOctXy88R8(q: THREE.Quaternion): number {
// Force the minimal representation (q.w >= 0) // Force the minimal representation (q.w >= 0)
const qnorm = q.clone().normalize(); const qnorm = tempNormalizedQuaternion.copy(q).normalize();
if (qnorm.w < 0) { if (qnorm.w < 0) {
qnorm.set(-qnorm.x, -qnorm.y, -qnorm.z, -qnorm.w); qnorm.set(-qnorm.x, -qnorm.y, -qnorm.z, -qnorm.w);
} }
@@ -984,8 +993,8 @@ export function encodeQuatOctXy88R8(q: THREE.Quaternion): number {
); );
const axis = const axis =
xyz_norm < 1e-6 xyz_norm < 1e-6
? new THREE.Vector3(1, 0, 0) ? tempAxis.set(1, 0, 0)
: new THREE.Vector3(qnorm.x, qnorm.y, qnorm.z).divideScalar(xyz_norm); : tempAxis.set(qnorm.x, qnorm.y, qnorm.z).divideScalar(xyz_norm);
// const foldAxis = (axis.z < 0); // const foldAxis = (axis.z < 0);
// --- Folded Octahedral Mapping (inline) --- // --- Folded Octahedral Mapping (inline) ---
@@ -1036,7 +1045,7 @@ export function decodeQuatOctXy88R8(
const t = Math.max(-f_z, 0); const t = Math.max(-f_z, 0);
f_x += f_x >= 0 ? -t : t; f_x += f_x >= 0 ? -t : t;
f_y += f_y >= 0 ? -t : t; f_y += f_y >= 0 ? -t : t;
const axis = new THREE.Vector3(f_x, f_y, f_z).normalize(); const axis = tempAxis.set(f_x, f_y, f_z).normalize();
// Decode the angle: θ ∈ [0,π] // Decode the angle: θ ∈ [0,π]
const theta = (angleInt / 255) * Math.PI; const theta = (angleInt / 255) * Math.PI;