Skip to content

VRAM usage with adding and removing WorldObjects #494

@kushalkolar

Description

@kushalkolar

I've been looking into VRAM usage with adding and removing WorldObjects, here's some things that I found:

With a Qt canvas it seems like WorldObjects are garbage collected and GPU VRAM is freed only after mouse movements on the canvas even after the WorldObject has visibly been removed from the scene.

This example uses nvidia-smi:

  1. Creates a 4096x4096 image when you double click on the canvas.
  2. Click on the image to remove it from the scene and call del on it. GPU VRAM is not freed.
  3. Make a mouse movement on the canvas after the image has been removed, GPU VRAM gets freed. But the GPU VRAM remains utilized until you make this mouse event.
import numpy as np
from wgpu.gui.auto import WgpuCanvas, run
import pygfx as gfx
import subprocess

canvas = WgpuCanvas()
renderer = gfx.WgpuRenderer(canvas)
scene = gfx.Scene()
camera = gfx.OrthographicCamera(5000, 5000)
camera.position.x = 2048
camera.position.y = 2048


def make_image():
    data = np.random.rand(4096, 4096).astype(np.float32)

    return gfx.Image(
        gfx.Geometry(grid=gfx.Texture(data, dim=2)),
        gfx.ImageBasicMaterial(clim=(0, 1)),
    )


def draw():
    renderer.render(scene, camera)
    canvas.request_draw()


def print_nvidia(msg=""):
    print(msg)
    print(
        subprocess.check_output(["nvidia-smi", "--format=csv", "--query-gpu=memory.used"]).decode().split("\n")[1]
    )
    print()


def add_img(*args):
    print_nvidia("Before creating image")
    img = make_image()
    print_nvidia("After creating image")
    scene.add(img)
    img.add_event_handler(remove_img, "click")
    draw()
    print_nvidia("After add image to scene")


def remove_img(*args):
    img = scene.children[0]
    scene.remove(img)
    draw()
    print_nvidia("After remove image from scene")
    del img
    draw()
    print_nvidia("After del image")
    renderer.add_event_handler(print_nvidia, "pointer_move")


renderer.add_event_handler(add_img, "double_click")

draw()
run()

This outputs:

Before creating image
73 MiB

After creating image
73 MiB

After add image to scene
137 MiB

After remove image from scene
137 MiB

After del image
137 MiB

<pygfx.objects._events.PointerEvent object at 0x7fa186d8f970>
73 MiB

<pygfx.objects._events.PointerEvent object at 0x7fa186d8f5e0>
73 MiB

To follow up from #382 , with jupyter the situation is more messy. As Korijn mentioned here: #382 (comment)

If anyone runs into issues with jupyter in the future, in fastplotlib I'm working on a workaround where all WorldObjects are stored in a dict that fastplotlib uses internally, and only weakref proxies are used to access the WorldObject so that they are never directly exposed to any references on the jupyter side: fastplotlib/fastplotlib#160

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions