Skip to content

Memory Leak related to unload and rendering #306

Description

@ScheiklP

Hi,
there is another memory leak, this time related to rendering in combination with simulation.unload.

As a testing scene, I took the pygame example from SofaPython3 and added a reload to every tenth step.

import Sofa
import Sofa.Core
import Sofa.Simulation
import Sofa.SofaGL
import SofaRuntime
import os

sofa_directory = os.environ["SOFA_ROOT"]
import pygame
from OpenGL.GL import *
from OpenGL.GLU import *

display_size = (800, 600)


def init_display(node):
    pygame.display.init()
    pygame.display.set_mode(display_size, pygame.DOUBLEBUF | pygame.OPENGL)

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glEnable(GL_LIGHTING)
    glEnable(GL_DEPTH_TEST)
    Sofa.SofaGL.glewInit()
    Sofa.Simulation.initVisual(node)
    Sofa.Simulation.initTextures(node)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45, (display_size[0] / display_size[1]), 0.1, 50.0)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()


def simple_render(rootNode):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glEnable(GL_LIGHTING)
    glEnable(GL_DEPTH_TEST)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45, (display_size[0] / display_size[1]), 0.1, 50.0)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    cameraMVM = rootNode.camera.getOpenGLModelViewMatrix()
    glMultMatrixd(cameraMVM)
    Sofa.SofaGL.draw(rootNode)

    pygame.display.get_surface().fill((0, 0, 0))
    pygame.display.flip()


def createScene(root):
    # Register all the common component in the factory.
    SofaRuntime.PluginRepository.addFirstPath(os.path.join(sofa_directory, "bin"))
    root.addObject("RequiredPlugin", name="SofaOpenglVisual")  # visual stuff
    root.addObject("RequiredPlugin", name="SofaLoader")  # geometry loaders
    root.addObject("RequiredPlugin", name="SofaSimpleFem")  # diffusion fem
    root.addObject("RequiredPlugin", name="SofaBoundaryCondition")  # constraints
    root.addObject("RequiredPlugin", name="SofaEngine")  # Box Roi
    root.addObject("RequiredPlugin", name="SofaImplicitOdeSolver")  # implicit solver
    root.addObject("RequiredPlugin", name="SofaMiscForceField")  # meshmatrix
    root.addObject("RequiredPlugin", name="SofaGeneralEngine")  # TextureInterpolation
    root.addObject("RequiredPlugin", name="CImgPlugin")  # for loading a bmp image for texture
    root.addObject("RequiredPlugin", name="SofaBaseLinearSolver")
    root.addObject("RequiredPlugin", name="SofaGeneralVisual")
    root.addObject("RequiredPlugin", name="SofaTopologyMapping")
    root.addObject("RequiredPlugin", name="SofaGeneralTopology")
    root.addObject("RequiredPlugin", name="SofaGeneralLoader")

    ### these are just some things that stay still and move around
    # so you know the animation is actually happening
    root.gravity = [0, -1.0, 0]
    root.addObject("VisualStyle", displayFlags="showAll")
    root.addObject("MeshGmshLoader", name="meshLoaderCoarse", filename="mesh/liver.msh")
    root.addObject("MeshObjLoader", name="meshLoaderFine", filename="mesh/liver-smooth.obj")

    root.addObject("EulerImplicitSolver")
    root.addObject("CGLinearSolver", iterations="200", tolerance="1e-09", threshold="1e-09")

    liver = root.addChild("liver")

    liver.addObject("TetrahedronSetTopologyContainer", name="topo", src="@../meshLoaderCoarse")
    liver.addObject("TetrahedronSetGeometryAlgorithms", template="Vec3d", name="GeomAlgo")
    liver.addObject("MechanicalObject", template="Vec3d", name="MechanicalModel", showObject="1", showObjectScale="3")

    liver.addObject("TetrahedronFEMForceField", name="fem", youngModulus="1000", poissonRatio="0.4", method="large")

    liver.addObject("MeshMatrixMass", massDensity="1")
    liver.addObject("FixedConstraint", indices="2 3 50")
    visual = liver.addChild("visual")
    visual.addObject("MeshObjLoader", name="meshLoader_0", filename="mesh/liver-smooth.obj", handleSeams="1")
    visual.addObject("OglModel", name="VisualModel", src="@meshLoader_0", color="red")
    visual.addObject("BarycentricMapping", input="@..", output="@VisualModel", name="visual mapping")

    # place light and a camera
    root.addObject("LightManager")
    root.addObject("DirectionalLight", direction=[0, 1, 0])
    root.addObject("InteractiveCamera", name="camera", position=[0, 15, 0], lookAt=[0, 0, 0], distance=37, fieldOfView=45, zNear=0.63, zFar=55.69)


if __name__ == "__main__":

    root = Sofa.Core.Node("myroot")
    createScene(root)
    Sofa.Simulation.init(root)

    with_window = True
    if with_window:
        init_display(root)

    for i in range(300):
        Sofa.Simulation.animate(root, root.getDt())
        Sofa.Simulation.updateVisual(root)
        if with_window:
            simple_render(root)
        if i % 10 == 0:
            if with_window:
                pygame.display.quit()
            Sofa.Simulation.unload(root)
            createScene(root)
            Sofa.Simulation.init(root)
            if with_window:
                init_display(root)

The leak seems to be related to the OpenGL context, because for 31 init_display calls, I get and AddressSanitizer output of

Indirect leak of 59521240 byte(s) in 31 object(s) allocated from:
    #0 0x7f8a1c4cf808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7f8a0be337c3 in SDL_malloc_REAL /sdl_build/SDL2-2.0.16/src/stdlib/SDL_malloc.c:5388

that matches the lost amount of memory.
And it also veeeery closely matches the amount of memory required to store 31 RGBA buffers of the set image size. :D

>>> (600*800*4*31)/59521240
0.9999791671006854

If no display is created, there is no leak.

@fredroy Do you maybe know what could be the issue here?

Cheers,
Paul

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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