From c988a62dce1890527638a884ccbf5e17ad1d8e7d Mon Sep 17 00:00:00 2001 From: Wouter van Boesschoten Date: Thu, 20 Apr 2023 17:10:18 +0200 Subject: [PATCH 1/5] If you make rommel, you clean up. --- src/platforms/browser/ImageWorker.mjs | 6 ++++++ src/platforms/browser/WebPlatform.mjs | 6 ++++++ src/renderer/c2d/C2dRenderer.mjs | 3 +++ src/renderer/c2d/C2dTextureTintManager.mjs | 3 +++ .../webgl/WebGLCoreRenderExecutor.mjs | 3 +++ src/renderer/webgl/WebGLRenderer.mjs | 6 ++++++ src/renderer/webgl/WebGLShaderProgram.mjs | 19 ++++++++++++++++++- src/tree/Stage.mjs | 14 ++++++++++++++ src/tree/TextureThrottler.mjs | 5 +++++ src/tree/core/CoreContext.mjs | 15 +++++++++++++++ src/tree/core/CoreRenderExecutor.mjs | 7 +++++++ 11 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/platforms/browser/ImageWorker.mjs b/src/platforms/browser/ImageWorker.mjs index 004f94ea..005a81ea 100644 --- a/src/platforms/browser/ImageWorker.mjs +++ b/src/platforms/browser/ImageWorker.mjs @@ -30,6 +30,12 @@ export default class ImageWorker { if (this._worker) { this._worker.terminate(); } + + this._items = null; + this._worker = null; + + delete this._items; + delete this._worker; } _initWorker() { diff --git a/src/platforms/browser/WebPlatform.mjs b/src/platforms/browser/WebPlatform.mjs index a7a588e5..0aa0ebf0 100644 --- a/src/platforms/browser/WebPlatform.mjs +++ b/src/platforms/browser/WebPlatform.mjs @@ -50,11 +50,17 @@ export default class WebPlatform { if (this._imageWorker) { this._imageWorker.destroy(); } + + clearInterval(this._loopHandler); + this._removeKeyHandler(); this._removeClickHandler(); this._removeHoverHandler(); this._removeScrollWheelHandler(); this._removeVisibilityChangeHandler(); + + this.stage = null; + delete this.stage; } startLoop() { diff --git a/src/renderer/c2d/C2dRenderer.mjs b/src/renderer/c2d/C2dRenderer.mjs index 01231cff..7f199535 100644 --- a/src/renderer/c2d/C2dRenderer.mjs +++ b/src/renderer/c2d/C2dRenderer.mjs @@ -38,6 +38,9 @@ export default class C2dRenderer extends Renderer { destroy() { this.tintManager.destroy(); + + this.tintManager = null; + delete this.tintManager; } _createDefaultShader(ctx) { diff --git a/src/renderer/c2d/C2dTextureTintManager.mjs b/src/renderer/c2d/C2dTextureTintManager.mjs index 3ad8715a..8406f9fb 100644 --- a/src/renderer/c2d/C2dTextureTintManager.mjs +++ b/src/renderer/c2d/C2dTextureTintManager.mjs @@ -27,6 +27,9 @@ export default class C2dTextureTintManager { destroy() { this.gc(true); + + this.stage = null; + delete this.stage; } _addMemoryUsage(delta) { diff --git a/src/renderer/webgl/WebGLCoreRenderExecutor.mjs b/src/renderer/webgl/WebGLCoreRenderExecutor.mjs index 175b5745..ac37535f 100644 --- a/src/renderer/webgl/WebGLCoreRenderExecutor.mjs +++ b/src/renderer/webgl/WebGLCoreRenderExecutor.mjs @@ -64,6 +64,9 @@ export default class WebGLCoreRenderExecutor extends CoreRenderExecutor { super.destroy(); this.gl.deleteBuffer(this._attribsBuffer); this.gl.deleteBuffer(this._quadsBuffer); + + this.gl = null; + delete this.gl; } _reset() { diff --git a/src/renderer/webgl/WebGLRenderer.mjs b/src/renderer/webgl/WebGLRenderer.mjs index 0e0728ea..1b142b81 100644 --- a/src/renderer/webgl/WebGLRenderer.mjs +++ b/src/renderer/webgl/WebGLRenderer.mjs @@ -46,6 +46,12 @@ export default class WebGLRenderer extends Renderer { destroy() { this.shaderPrograms.forEach(shaderProgram => shaderProgram.destroy()); + + this.shaderPrograms = null; + this._compressedTextureExtensions = null; + + delete this.shaderPrograms; + delete this._compressedTextureExtensions; } _createDefaultShader(ctx) { diff --git a/src/renderer/webgl/WebGLShaderProgram.mjs b/src/renderer/webgl/WebGLShaderProgram.mjs index 0947fe66..e6a78603 100644 --- a/src/renderer/webgl/WebGLShaderProgram.mjs +++ b/src/renderer/webgl/WebGLShaderProgram.mjs @@ -28,6 +28,7 @@ export default class WebGLShaderProgram { this.fragmentShaderSource = fragmentShaderSource; this._program = null; + this.gl = null; this._uniformLocations = new Map(); this._attributeLocations = new Map(); @@ -109,8 +110,24 @@ export default class WebGLShaderProgram { destroy() { if (this._program) { this.gl.deleteProgram(this._program); - this._program = null; } + + this._attributeLocations = null; + this._currentUniformValues = null; + this.fragmentShaderSource = null; + this._program = null; + this.gl = null; + this._uniformLocations = null; + this.vertexShaderSource = null; + + delete this.vertexShaderSource; + delete this._program; + delete this._currentUniformValues; + delete this.fragmentShaderSource; + delete this.gl; + delete this._uniformLocations; + delete this._attributeLocations; + } get glProgram() { diff --git a/src/tree/Stage.mjs b/src/tree/Stage.mjs index 7457f685..7925ffc5 100644 --- a/src/tree/Stage.mjs +++ b/src/tree/Stage.mjs @@ -223,6 +223,20 @@ export default class Stage extends EventEmitter { this.ctx.destroy(); this.textureManager.destroy(); this._renderer.destroy(); + + this.gl = null; + this.c2d = null; + this.ctx = null; + this.platform = null; + this.textureManager = null; + this._renderer = null; + + delete this.gl; + delete this.c2d; + delete this.ctx; + delete this.platform; + delete this.textureManager; + delete this._renderer; } stop() { diff --git a/src/tree/TextureThrottler.mjs b/src/tree/TextureThrottler.mjs index e90f0a69..fe4c34f9 100644 --- a/src/tree/TextureThrottler.mjs +++ b/src/tree/TextureThrottler.mjs @@ -36,6 +36,11 @@ export default class TextureThrottler { destroy() { this._sources = []; this._data = []; + this.stage = null; + + delete this._sources; + delete this._data; + delete this.stage; } processSome() { diff --git a/src/tree/core/CoreContext.mjs b/src/tree/core/CoreContext.mjs index 73703525..4d91c615 100644 --- a/src/tree/core/CoreContext.mjs +++ b/src/tree/core/CoreContext.mjs @@ -49,6 +49,21 @@ export default class CoreContext { destroy() { this._renderTexturePool.forEach(texture => this._freeRenderTexture(texture)); this._usedMemory = 0; + + this.stage = null; + this.root = null; + + this.renderState = null; + this.renderExec = null; + this._renderTexturePool = null; + this._zSorts = null; + + delete this.stage; + delete this.root; + delete this.renderState; + delete this.renderExec; + delete this._renderTexturePool; + delete this._zSorts; } hasRenderUpdates() { diff --git a/src/tree/core/CoreRenderExecutor.mjs b/src/tree/core/CoreRenderExecutor.mjs index 8dfe77f8..b0c29662 100644 --- a/src/tree/core/CoreRenderExecutor.mjs +++ b/src/tree/core/CoreRenderExecutor.mjs @@ -29,6 +29,13 @@ export default class CoreRenderExecutor { } destroy() { + this.ctx = null; + this.renderState = null; + this.gl = null; + + delete this.ctx; + delete this.renderState; + delete this.gl; } _reset() { From 06a33db5c4cc42e75d8e92943cb486529d4f0a83 Mon Sep 17 00:00:00 2001 From: Wouter van Boesschoten Date: Thu, 20 Apr 2023 18:02:43 +0200 Subject: [PATCH 2/5] Add test example --- examples/destroy/index.html | 95 +++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 examples/destroy/index.html diff --git a/examples/destroy/index.html b/examples/destroy/index.html new file mode 100644 index 00000000..f3e48ca2 --- /dev/null +++ b/examples/destroy/index.html @@ -0,0 +1,95 @@ + + + + + + + + + + + + From 43498e75fcb0f7e4577010ba15c5610555c1119b Mon Sep 17 00:00:00 2001 From: erikhaandrikman Date: Fri, 21 Apr 2023 09:51:24 +0200 Subject: [PATCH 3/5] Clear last rendered frame --- examples/destroy/index.html | 1 + src/tree/Stage.mjs | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/examples/destroy/index.html b/examples/destroy/index.html index f3e48ca2..7c6167ae 100644 --- a/examples/destroy/index.html +++ b/examples/destroy/index.html @@ -79,6 +79,7 @@ document.addEventListener("keydown", (e) => { + // spacebar if (e.keyCode === 32) { if (app) { app.destroy(); diff --git a/src/tree/Stage.mjs b/src/tree/Stage.mjs index 7925ffc5..656f58f5 100644 --- a/src/tree/Stage.mjs +++ b/src/tree/Stage.mjs @@ -132,7 +132,7 @@ export default class Stage extends EventEmitter { try { return !!window.WebGLRenderingContext; - } catch(e) { + } catch (e) { return false; } } @@ -180,7 +180,7 @@ export default class Stage extends EventEmitter { opt('memoryPressure', 24e6); opt('bufferMemory', 2e6); opt('textRenderIssueMargin', 0); - opt('fontSharp',{precision:0.6666666667, fontSize: 24}) + opt('fontSharp', { precision: 0.6666666667, fontSize: 24 }) opt('clearColor', [0, 0, 0, 0]); opt('defaultFontFace', 'sans-serif'); opt('fixedDt', 0); @@ -224,6 +224,16 @@ export default class Stage extends EventEmitter { this.textureManager.destroy(); this._renderer.destroy(); + // clear last rendered frame + if (this.gl) { + this.gl.clearColor(0.0, 0.0, 0.0, 0.0); + this.gl.clear(this.gl.COLOR_BUFFER_BIT); + } else if (this.c2d) { + this.c2d.clearRect( + 0, 0, this.c2d.canvas.width, this.c2d.canvas.height + ); + } + this.gl = null; this.c2d = null; this.ctx = null; @@ -510,10 +520,10 @@ export default class Stage extends EventEmitter { } } - getChildrenByPosition(x, y){ + getChildrenByPosition(x, y) { const children = []; this.root.core.update(); - this.root.core.collectAtCoord(x,y,children); + this.root.core.collectAtCoord(x, y, children); return children; } From e0156dbd1119ff28591ee221777e284e1b2b57ab Mon Sep 17 00:00:00 2001 From: wouterlucas Date: Fri, 21 Apr 2023 11:33:45 +0200 Subject: [PATCH 4/5] Clean options too --- src/tree/Stage.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tree/Stage.mjs b/src/tree/Stage.mjs index 656f58f5..fa4c3c82 100644 --- a/src/tree/Stage.mjs +++ b/src/tree/Stage.mjs @@ -237,6 +237,7 @@ export default class Stage extends EventEmitter { this.gl = null; this.c2d = null; this.ctx = null; + this._options = null; this.platform = null; this.textureManager = null; this._renderer = null; @@ -244,6 +245,7 @@ export default class Stage extends EventEmitter { delete this.gl; delete this.c2d; delete this.ctx; + delete this._options; delete this.platform; delete this.textureManager; delete this._renderer; From 2de9550978ea0d90b4b36b34a2001f60b2471f15 Mon Sep 17 00:00:00 2001 From: Michiel van der Geest Date: Fri, 21 Apr 2023 16:25:37 +0200 Subject: [PATCH 5/5] Tagged 2.9.1 for hotfix release and updated changelog. --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93853468..5da1e24c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## v2.9.1 + +*21 apr 2023* + +- 🔥 Hotfix for memory leak when `pauseRafLoopOnIdle` is enabled (introduced in v2.7.0) +- Implemented additional cleanup of Lightning code that gets stuck on the heap after calling `destroy` + ## v2.9.0 *16 feb 2023* diff --git a/package-lock.json b/package-lock.json index feeeee22..2e09a6e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@lightningjs/core", - "version": "2.9.0", + "version": "2.9.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@lightningjs/core", - "version": "2.9.0", + "version": "2.9.1", "license": "Apache-2.0", "devDependencies": { "@babel/core": "^7.8.3", diff --git a/package.json b/package.json index a309ad20..3605ba21 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "Metrological, Bas van Meurs ", "name": "@lightningjs/core", - "version": "2.9.0", + "version": "2.9.1", "license": "Apache-2.0", "main": "dist/lightning.js", "module": "index.js",