Engine resize, iOS crash, and post-process texture caching

Less of a question and more of a sharing a root cause we found…

Our app is a configurator, and only renders when the camera moves to save the mobile devices battery. In some implementations, we also have a use case where we need to resize the engine in response to UX changes (making room for drawers opening etc)

We are resizing the engine in response to every ResizeObserver call to make it all appear smooth (customer request) - but as it turned out, this causes Mobile Safari to crash frequently to enormous memory usage.

Turns out the root cause is in… Post-Processors. Those cache textures for at least 100-ish frames, so rapidly resizing the engine accumulates A LOT of temporary caches. Compounded by infrequent renders, those rarely got cleaned up, so when another drawer opens, we’d have another huge spike

postProcess.ts:
if (currentRenderId - this._textureCache[i].lastUsedRenderId > 100) {

(this includes FXAA, image effects, highlights, etc)

Monkey-patching and force flushing the cache on every resize made it jerkier, so for now our workaround is to render 100±ish frames after resizing…

Memory (resizeBefore): Texture 145.3MB/82, Buffer 4.9MB, Total 302.5MB

Memory (resizeAfter): Texture 600.7MB/96, Buffer 4.9MB, Total 893.7MB

Memory (updateTextures): Texture 600.7MB/96, Buffer 4.9MB, Total 893.7MB
Memory (updateTextures): Texture 545.1MB/94, Buffer 4.9MB, Total 824.2MB
Memory (updateTextures): Texture 487.9MB/92, Buffer 4.9MB, Total 752.7MB
Memory (updateTextures): Texture 429.1MB/90, Buffer 4.9MB, Total 679.3MB
Memory (updateTextures): Texture 365.2MB/88, Buffer 4.9MB, Total 599.3MB
Memory (updateTextures): Texture 299.0MB/86, Buffer 4.9MB, Total 516.6MB
Memory (updateTextures): Texture 230.2MB/84, Buffer 4.9MB, Total 430.7MB
Memory (updateTextures): Texture 160.0MB/82, Buffer 4.9MB, Total 342.9MB


Shout out to GitHub - greggman/webgl-memory: A library to track webgl-memory for helping to track down those memory issues!

2 Likes

Thanks a lot for sharing it, I wonder how we could improve/workaround the issue internally :frowning:
cc @Deltakosh

Minor update with fresh Monday look… Not sure what caused the lag in the last attempt, but doing this before the resize() call seems to keep memory low and still gives a nice smooth resize animation:

NOTE: Do check if the canvas size actually changed, otherwise if you blow away caches you’ll end up with a black screen.

if (this.scene) {
    if (this.scene.postProcesses) {
        for (let i = 0; i < this.scene.postProcesses.length; i += 1) {
            this.scene.postProcesses[i]._disposeTextureCache();
        }
    }
}

if (this.pipeline.fxaa) {
    this.pipeline.fxaa._disposeTextureCache();
}

if (this.pipeline.imageProcessing) {
    this.pipeline.imageProcessing._disposeTextureCache();
}
1 Like