Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 19
Add mesh limits and clone recycling to prevent memory overflow#402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
a8d37ecabfc786cb06b76215fa1786002f5cf5e2ebce03ed8File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -251,6 +251,9 @@ export const flockShapes = { | ||
| boxId = boxId + "_" + flock.scene.getUniqueId(); | ||
| } | ||
| if (flock.maxMeshesReached()) return null; | ||
| flock._recycleOldestByKey(blockKey); | ||
coderabbitai[bot] marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| const dimensions = { width, height, depth }; | ||
| // Retrieve cached VertexData or create it if this is the first instance | ||
| @@ -289,6 +292,7 @@ export const flockShapes = { | ||
| flock.applyPhysics(newBox, boxShape); | ||
| flock.announceMeshReady(newBox.name, groupName); | ||
| flock._registerInstance(blockKey, newBox.name); | ||
| if (callback) { | ||
| requestAnimationFrame(() => callback()); | ||
| @@ -325,6 +329,9 @@ export const flockShapes = { | ||
| sphereId = sphereId + "_" + flock.scene.getUniqueId(); | ||
| } | ||
| if (flock.maxMeshesReached()) return null; | ||
| flock._recycleOldestByKey(blockKey); | ||
| const dimensions = { diameterX, diameterY, diameterZ }; | ||
| // Retrieve cached VertexData or create it if this is the first instance | ||
| @@ -361,6 +368,7 @@ export const flockShapes = { | ||
| flock.applyPhysics(newSphere, sphereShape); | ||
| flock.announceMeshReady(newSphere.name, groupName); | ||
| flock._registerInstance(blockKey, newSphere.name); | ||
| if (callback) { | ||
| requestAnimationFrame(() => callback()); | ||
| @@ -406,6 +414,9 @@ export const flockShapes = { | ||
| cylinderId = cylinderId + "_" + flock.scene.getUniqueId(); | ||
| } | ||
| if (flock.maxMeshesReached()) return null; | ||
| flock._recycleOldestByKey(blockKey); | ||
| // Get or create cached VertexData | ||
| const vertexData = flock.getOrCreateGeometry( | ||
| "Cylinder", | ||
| @@ -448,6 +459,7 @@ export const flockShapes = { | ||
| flock.applyPhysics(newCylinder, cylinderShape); | ||
| flock.announceMeshReady(newCylinder.name, groupName); | ||
| flock._registerInstance(blockKey, newCylinder.name); | ||
| if (callback) { | ||
| requestAnimationFrame(() => callback()); | ||
| @@ -484,6 +496,9 @@ export const flockShapes = { | ||
| capsuleId = capsuleId + "_" + flock.scene.getUniqueId(); | ||
| } | ||
| if (flock.maxMeshesReached()) return null; | ||
| flock._recycleOldestByKey(blockKey); | ||
| // Get or create cached VertexData | ||
| const vertexData = flock.getOrCreateGeometry( | ||
| "Capsule", | ||
| @@ -535,6 +550,7 @@ export const flockShapes = { | ||
| flock.applyPhysics(newCapsule, capsuleShape); | ||
| flock.announceMeshReady(newCapsule.name, groupName); | ||
| flock._registerInstance(blockKey, newCapsule.name); | ||
| if (callback) { | ||
| requestAnimationFrame(() => callback()); | ||
| @@ -558,6 +574,9 @@ export const flockShapes = { | ||
| planeId = planeId + "_" + flock.scene.getUniqueId(); | ||
| } | ||
| if (flock.maxMeshesReached()) return null; | ||
| flock._recycleOldestByKey(blockKey); | ||
| const newPlane = flock.BABYLON.MeshBuilder.CreatePlane( | ||
| planeId, | ||
| { | ||
| @@ -610,6 +629,7 @@ export const flockShapes = { | ||
| newPlane.metadata.blockKey = blockKey; | ||
| flock.announceMeshReady(newPlane.name, groupName); | ||
| flock._registerInstance(blockKey, newPlane.name); | ||
| if (callback) { | ||
| requestAnimationFrame(() => callback()); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -94,6 +94,9 @@ export const flock = { | ||
| meshDebug: false, | ||
| performanceOverlay: false, | ||
| maxMeshes: 5000, | ||
| maxClonesPerSource: 500, | ||
| meshLimitEnabled: false, | ||
| meshRecyclingEnabled: false, | ||
| console: console, | ||
| havokAbortHandled: false, | ||
| triggerHandlingDebug: false, | ||
| @@ -207,7 +210,37 @@ export const flock = { | ||
| return errorContext; | ||
| }, | ||
| // Prune disposed entries and auto-recycle the oldest live instance | ||
| // when the per-key cap is hit. Used by all mesh creation paths. | ||
| // Only active when flock.meshRecyclingEnabled is true. | ||
| _recycleOldestByKey(key) { | ||
| if (!flock.meshRecyclingEnabled) return; | ||
| if (!flock._modelInstances) flock._modelInstances = Object.create(null); | ||
| const current = Array.isArray(flock._modelInstances[key]) | ||
| ? flock._modelInstances[key] | ||
| : []; | ||
| flock._modelInstances[key] = current.filter((name) => { | ||
| const m = flock.scene?.getMeshByName(name); | ||
| return m && !m.isDisposed(); | ||
| }); | ||
| const max = flock.maxClonesPerSource ?? 500; | ||
| if (flock._modelInstances[key].length >= max) { | ||
| const oldestName = flock._modelInstances[key][0]; | ||
| const oldest = flock.scene?.getMeshByName(oldestName); | ||
| if (oldest) flock.disposeMesh(oldest); | ||
| flock._modelInstances[key] = | ||
| flock._modelInstances[key].slice(1); | ||
| } | ||
| }, | ||
| _registerInstance(key, meshName) { | ||
| if (!flock._modelInstances) flock._modelInstances = Object.create(null); | ||
| const current = Array.isArray(flock._modelInstances[key]) | ||
| ? flock._modelInstances[key] | ||
| : []; | ||
| flock._modelInstances[key] = current.concat(meshName); | ||
| }, | ||
coderabbitai[bot] marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| maxMeshesReached() { | ||
| if (!flock.meshLimitEnabled) return false; | ||
| const scene = flock?.scene; | ||
| if (!scene || typeof flock.maxMeshes !== "number") return false; | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check remaining mesh capacity before operations that add multiple meshes.
The
maxMeshesReached()checks at lines 277, 299, and 309 only verify the current total against the limit. However, the subsequent operations—container.addAllToScene()(line 283),container.addToScene()(line 313), and especiallycontainer.instantiateHierarchy()(line 338)—can add multiple meshes in a single call. A scene with few remaining slots can overshoot themaxMesheslimit by adding an entire cloned hierarchy or container, even though the check passed moments before. Since these operations may also be called after async waits or loads (visible in the function flow), the check state can become stale between the validation and the actual mesh addition.🤖 Prompt for AI Agents