Headless Rendering Memory Usage

Hello everyone.

I am using headless-gl to do some offscreen rendering.

In my case I have a scene where add some objects to it, take a screen shot and then add more objects and then take another screen shot and so on, the main purpose is to capture some informative images where it shows the user steps to make something, the objects are simple boxes with some wireframes.

The problem is that memory usage is building up upon calling render.render(), the reason I know that I know calling the render method is the problem is because memory builds up when running a simple loop with an index with nothing other than the render method.

After ~30 calls the memory usage gets around 3.7GB and then the NestJS server throws a heap error.

Note that this issue doesn’t happen when calling the exact same code but in the browser.

here is my code.

THREE.ColorManagement.enabled = false;

const offscreenCanvas = {
    canvasWidth,
    canvasHeight,
    // @ts-ignore
    addEventListener: (event: any) => {},
    // @ts-ignore
    removeEventListener: (event: any) => {},
} as unknown as OffscreenCanvas;

// @ts-ignore
this.renderer = new THREE.WebGLRenderer({
    canvas: offscreenCanvas,
    antialias: false,
    powerPreference: "low-performance",
    // @ts-ignore
    context: gl(canvasWidth, canvasHeight),
});

this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default PCFShadowMap

const renderTarget = new THREE.WebGLRenderTarget(
    canvasWidth,
    canvasHeight,
    {
        minFilter: THREE.LinearFilter,
        magFilter: THREE.NearestFilter,
        format: THREE.RGBAFormat,
        type: THREE.UnsignedByteType,
    }
);

this.renderer.setRenderTarget(renderTarget);

and here is the render loop:

let images: string[] = [];
// this is a generator
let stepper = getSteps();

for (const func of stepper) {
    renderer.render();
    console.log(`saved ${index}.jpg`);
    images.push(this.engine.renderer.getBase64Screenshot());
}

return images;

Sounds like a memory leak, but hard to say from the code snippets. How do you create and dispose the scene? Do you use a single renderer for each capture, or create new one each time? Do you release Blob URLs after they are no longer necessary?

it was a memory leak, some other part of the code had a bug that created too many objects, solved that and now it looks good.
I guess I know now that ThreeJS has a limit of 70K objects in a scene :slight_smile: .