GreasedLineMesh: preallocate buffers

Background

Currently GreasedLineMesh._setPoints allocates many buffers for each new TypedArray call, there is even memory allocation in loop, this preallocated the buffer needed to reduce memory pressure.

Proposed change

Note that this is not benchmarked yet.

diff --git a/packages/dev/core/src/Meshes/GreasedLine/greasedLineMesh.ts b/packages/dev/core/src/Meshes/GreasedLine/greasedLineMesh.ts
index e6e0a5fecb..dc456c83a9 100644
--- a/packages/dev/core/src/Meshes/GreasedLine/greasedLineMesh.ts
+++ b/packages/dev/core/src/Meshes/GreasedLine/greasedLineMesh.ts
@@ -93,19 +93,28 @@ export class GreasedLineMesh extends GreasedLineBaseMesh {
         let vertexPositionsLen = 0,
             indicesLength = 0,
             uvLength = 0,
-            previousAndSideLength = 0;
+            previousAndSideLength = 0,
+            maxLineLength = 0;
         for (const p of points) {
+            maxLineLength = Math.max(p.length, maxLineLength);
             vertexPositionsLen += p.length * 2;
             indicesLength += (p.length - 3) * 2;
             uvLength += (p.length * 4) / 3;
             previousAndSideLength += (p.length * 8) / 3;
         }
-        const vertexPositionsArr = new Float32Array(vertexPositionsLen);
-        const indicesArr = vertexPositionsLen > 65535 ? new Uint32Array(indicesLength) : new Uint16Array(indicesLength);
-        const uvArr = new Float32Array(uvLength);
-        const previousAndSide = new Float32Array(previousAndSideLength);
+        const buffer = new ArrayBuffer(vertexPositionsLen * 4 + indicesLength * (vertexPositionsLen > 65535 ? 4 : 2) + uvLength * 4 + previousAndSideLength * 4 * 2);
+        const tempBuffer = new ArrayBuffer((maxLineLength / 3 + maxLineLength * 4) * 4);
+        let byteOffset = 0;
+        const vertexPositionsArr = new Float32Array(buffer, byteOffset, vertexPositionsLen);
+        byteOffset += vertexPositionsArr.byteLength;
+        const indicesArr = vertexPositionsLen > 65535 ? new Uint32Array(buffer, byteOffset, indicesLength) : new Uint16Array(buffer, byteOffset, indicesLength);
+        byteOffset += indicesArr.byteLength;
+        const uvArr = new Float32Array(buffer, byteOffset, uvLength);
+        byteOffset += uvArr.byteLength;
+        const previousAndSide = new Float32Array(buffer, byteOffset, previousAndSideLength);
+        byteOffset += previousAndSide.byteLength;
         // it's the same length here
-        const nextAndCounters = new Float32Array(previousAndSideLength);
+        const nextAndCounters = new Float32Array(buffer, byteOffset, previousAndSideLength);
         let vertexPositionsOffset = 0,
             indicesOffset = 0,
             uvOffset = 0,
@@ -113,7 +122,7 @@ export class GreasedLineMesh extends GreasedLineBaseMesh {
             nextAndCountersOffset = 0;
 
         for (const p of points) {
-            const lengthArray = GreasedLineTools.GetLineLengthArray(p);
+            const lengthArray = GreasedLineTools.GetLineLengthArray(p, tempBuffer);
             const totalLength = lengthArray[lengthArray.length - 1];
             for (let j = 0, jj = 0; jj < p.length; j++, jj += 3) {
                 const baseOffset = vertexPositionsOffset + jj * 2;
@@ -142,8 +151,10 @@ export class GreasedLineMesh extends GreasedLineBaseMesh {
             vertexPositionsOffset += currVertexPositionsOffsetLength;
             indicesOffset += (p.length - 3) * 2;
 
-            const previous = new Float32Array(positions.length);
-            const next = new Float32Array(positions.length);
+            let byteOffset = lengthArray.byteLength;
+            const previous = new Float32Array(tempBuffer, byteOffset, positions.length);
+            byteOffset += previous.byteLength;
+            const next = new Float32Array(tempBuffer, byteOffset, positions.length);
             const l = positions.length / 6;
             let v;
             if (GreasedLineMesh._CompareV3(0, l - 1, positions)) {
diff --git a/packages/dev/core/src/Misc/greasedLineTools.ts b/packages/dev/core/src/Misc/greasedLineTools.ts
index 2fdbc9a6cc..a05130198f 100644
--- a/packages/dev/core/src/Misc/greasedLineTools.ts
+++ b/packages/dev/core/src/Misc/greasedLineTools.ts
@@ -270,10 +270,11 @@ export class GreasedLineTools {
     /**
      * Gets the the length from the beginning to each point of the line as array.
      * @param data array of line points
+     * @param buf optional pre-allocated buffer to reduce memory pressure
      * @returns length array of the line
      */
-    public static GetLineLengthArray(data: number[]): Float32Array {
-        const out = new Float32Array(data.length / 3);
+    public static GetLineLengthArray(data: number[], buf?: ArrayBuffer): Float32Array {
+        const out = buf ? new Float32Array(buf, 0, data.length / 3) : new Float32Array(data.length / 3);
         let length = 0;
         for (let index = 0, pointsLength = data.length / 3 - 1; index < pointsLength; index++) {
             let x = data[index * 3 + 0];

Testing

Just copy-paste code after const {GreasedLineTools, GreasedLineMesh} = BABYLON; to any playground to test it.

1 Like

cc @roland

1 Like

Thanks for the suggestion! I’ll only be able to open a PR in about 10 days. If you’d like to have the change sooner, please feel free to go ahead and submit a PR, totally fine with me :blush:

2 Likes