vtkRenderWindow memory leak

Hi VTK Team

I encounter a memory leak when use the code below, RAM utilization is going up.

It seems that vtkRenderWindow is not freed correctly. If I put out of the loop vtkRenderwindow, everything is ok.

Also, It seems to be an old problem as you can see here (RAM blow out when looping vtkRenderWindow render). This problem is still present on the last release.

def test_memory_leak():
    colors = vtkNamedColors()

    # create source
    source = vtkSphereSource()
    source.SetCenter(0, 0, 0)
    source.SetRadius(5.0)
    source.SetPhiResolution(30)
    source.SetThetaResolution(30)

    # mapper
    mapper = vtkPolyDataMapper()
    mapper.SetInputConnection(source.GetOutputPort())

    # actor
    actor = vtkActor()
    actor.GetProperty().SetColor(colors.GetColor3d('IndianRed'))
    actor.GetProperty().SetSpecular(0.6)
    actor.GetProperty().SetSpecularPower(30)
    actor.SetMapper(mapper)

    for time in range(200):
        # create a rendering window and renderer
        ren = vtkRenderer()
        renWin = vtkRenderWindow()
        renWin.AddRenderer(ren)
        renWin.SetWindowName('Screenshot')

        # create a renderwindowinteractor
        iren = vtkRenderWindowInteractor()
        iren.SetRenderWindow(renWin)

        #renwin offscreen
        renWin.SetOffScreenRendering(1)

        # assign actor to the renderer
        ren.AddActor(actor)
        ren.SetBackground(colors.GetColor3d('MistyRose'))

        renWin.Render()

        # screenshot code:
        w2if = vtkWindowToImageFilter()
        w2if.SetInput(renWin)
        w2if.SetInputBufferTypeToRGB()
        w2if.ReadFrontBufferOff()
        w2if.Update()

        writer = vtkPNGWriter()
        writer.SetFileName('file%02d.png' % time)
        writer.SetInputConnection(w2if.GetOutputPort())
        writer.Write()

test_memory_leak()

I am on macOsX M1, using rosetta system (x86_64), python 3.9.13, vtk 9.2.5

Not sure, but it seems related and they are working on it : https://gitlab.kitware.com/vtk/vtk/-/issues/18573

Hello,

I’m not sure whether that such test can realiably diagnose a leak. I see a loop that creates 400 objects and Python’s garbage colletion and memory management are a black box. I believe you can try forcing a GC after running the loop and then measure memory consumption.

best,

Paulo

Thank you for your feedback @Paulo_Carvalho

I followed your advice, and simplify the loop. I still observe a leak. The RAM is slowly increasing. See below the code:

def test_memory_leak():
      for time in range(200):
              gc.collect()
              ren = vtkRenderer()
              renWin = vtkRenderWindow()
              renWin.AddRenderer(ren)
              renWin.SetWindowName('Screenshot')
              gc.collect() 


test_memory_leak()

It’s been a very long time, I did not need to touch C++. I will try to find the time to reproduce this example and see if I observe this leak in C++ also.

This user is also testing memory leaks, in C++ code: Memory leak occurs if I use vtkSmartPointer in sub-thread. - #2 by sohnishi
You could take inspiration from his code + VTK examples + VTK_DEBUG_LEAKS

how about it in C++?

I have just encountered this same issue in c++. Version 9.2.x, MacOS, Intel silicon. The render window is not closing, possibly having to do with using vtkPNGWriter and no window interactor. When run in lldb, after 100 iterations a warning pops up saying “[Window] WARNING: NSWindow has detected an excessive live window count of 101. Window 0x1e69 of class ‘NSWindow’ created after passing the threshold of 100.” I’ll post the backtrace at the bottom here.

I encountered this at first creating the renderWindow inside a function that is looped, then tried a workaround where a single renderWindow is passed into the function inside the loop. In every case, Finalize() is supposed to free the resources, but does not. More troubling is the fact that even the object going out of scope (when inside the function it was wrapped in a vtkNew) does not free the resources.

As a side note, I tried to run VTK_DEBUG_LEAKS but couldn’t get it to report anything. If anyone knows a good tutorial that would be really helpful.

[Window] WARNING: NSWindow has detected an excessive live window count of 101. Window 0x1e69 of class 'NSWindow' created after passing the threshold of 100. This window is not necessarily the cause, and this warning will only be shown once per window class. (
        0   AppKit                              0x00007ff811a41b35 -[NSWindow _setWindowNumber:] + 837
        1   AppKit                              0x00007ff8123cc4ef _NXCreateWindow + 278
        2   AppKit                              0x00007ff811b4d71e -[NSWindow _commonAwake] + 871
        3   AppKit                              0x00007ff811a40b5f -[NSWindow _commonInitFrame:styleMask:backing:defer:] + 1209
        4   AppKit                              0x00007ff811a40290 -[NSWindow _initContent:styleMask:backing:defer:contentView:] + 1047
        5   AppKit                              0x00007ff811a3fe72 -[NSWindow initWithContentRect:styleMask:backing:defer:] + 42
        6   libvtkRenderingOpenGL2-9.2.9.2.6.dy 0x0000000103c60d4d _ZN20vtkCocoaRenderWindow13CreateAWindowEv + 1859
        7   libvtkRenderingOpenGL2-9.2.9.2.6.dy 0x0000000103cd1c61 _ZN21vtkOpenGLRenderWindow5StartEv + 31
        8   libvtkRenderingOpenGL2-9.2.9.2.6.dy 0x0000000103c6137b _ZN20vtkCocoaRenderWindow5StartEv + 15
        9   libvtkRenderingCore-9.2.9.2.6.dylib 0x000000010c049f5d _ZN15vtkRenderWindow6RenderEv + 169
        10  libvtkRenderingOpenGL2-9.2.9.2.6.dy 0x0000000103cd3d27 _ZN21vtkOpenGLRenderWindow6RenderEv + 73
        11  genVTKimages                        0x000000010005e33f _ZN8swiftLib14testVTKDataVizE15vtkSmartPointerI15vtkRenderWindowERNSt3__16vectorIS0_I11vtkPolyDataENS3_9allocatorIS6_EEEESA_PNS4_IdNS7_IdEEEEmmPNS3_12basic_stringIcNS3_11char_traitsIcEENS7_IcEEEE + 2191
        12  genVTKimages                        0x000000010005f115 _ZN8swiftLib14testVTKDataVizE15vtkSmartPointerI15vtkRenderWindowES0_I11vtkPolyDataES4_PNSt3__16vectorIdNS5_9allocatorIdEEEEmmPNS5_12basic_stringIcNS5_11char_traitsIcEENS7_IcEEEE + 437
        13  genVTKimages                        0x000000010005d751 _ZN8swiftLib14testVTKDataVizE15vtkSmartPointerI15vtkRenderWindowES0_I11vtkPolyDataES0_I9vtkPointsEPNSt3__16vectorIdNS7_9allocatorIdEEEEmmPNS7_12basic_stringIcNS7_11char_traitsIcEENS9_IcEEEE + 209
        14  genVTKimages                        0x000000010005d5ff _ZN8swiftLib17dataVizComponents11writeOutImgENSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE15vtkSmartPointerI15vtkRenderWindowE + 143
        15  genVTKimages                        0x000000010005d992 _ZN8swiftLib17dataVizComponents11writeOutImgE15vtkSmartPointerI15vtkRenderWindowE + 98
        16  genVTKimages                        0x0000000100041e15 main + 7237
        17  dyld                                0x00007ff80e5fc41f start + 190

3

Can you, please, share the C++ code of your test?

Here it is, as an MRE. Note that the problem persists even if, as here, the vtkRenderWindow is rendering a blank scene. The preprocessor directives are to quickly verify the issue exists whether one vtkRenderWindow is instantiated for the entire loop and passed in, or within the looped function.

#include <sstream>
#include <vtkPNGWriter.h>
#include <vtkRenderWindow.h>
#include <vtkSmartPointer.h>
#include <vtkWindowToImageFilter.h>

// only turn one of these on at a time
//#define ONE_RENDERER // instantiate vtkRenderWindow outside the loop and pass into fcn
#define MANY_RENDERERS // instantiate vtkRenderWindow inside fcn

void testVTKDataViz(
#ifdef ONE_RENDERER
    vtkSmartPointer<vtkRenderWindow> renWin,
#endif
    std::string OPpath) {
        
#ifdef MANY_RENDERERS
        vtkNew<vtkRenderWindow> renWin;
        renWin->OffScreenRenderingOn();
#endif
        renWin->Render();
        
        vtkNew<vtkWindowToImageFilter> imgf;
        imgf->SetInput(renWin);
        imgf->SetInputBufferTypeToRGB();
        imgf->Update();
        
        vtkNew<vtkPNGWriter> wr;
        wr->SetFileName(OPpath.c_str());
        wr->SetInputConnection(imgf->GetOutputPort());
        wr->Write();
        
#ifdef MANY_RENDERERS
        renWin->Finalize();
#endif
}

int main() {

#ifdef ONE_RENDERER
        auto renWin = vtkSmartPointer<vtkRenderWindow>::New();
        renWin->OffScreenRenderingOn();
#endif
        std::string test_dir = "./imgDump";
        
        for (size_t ii=0; ii<1500; ii++) {
                std::stringstream ssPath;
                ssPath << test_dir << "/sphere_" << ii << ".png";
                std::cout << ssPath.str() << std::endl;
#ifdef MANY_RENDERERS
                testVTKDataViz(ssPath.str());
#endif
#ifdef ONE_RENDERER
                testVTKDataViz(renWin, ssPath.str());
                renWin->Finalize();
#endif
        }
        
        return EXIT_SUCCESS;
}

Does the crash occur with ONE_RENDERER or MANY_RENDERERS?

All those #defines are obfuscating the code.

It occurs in either scenario, that’s why I included them.

Ok. Can you, please, post the simpler (enabled by ONE_RENDERER) one?

Thank you

Sure:

#include <sstream>
#include <vtkPNGWriter.h>
#include <vtkRenderWindow.h>
#include <vtkSmartPointer.h>
#include <vtkWindowToImageFilter.h>


void testVTKDataViz(ER
    vtkSmartPointer<vtkRenderWindow> renWin,
    std::string OPpath) {
        
        renWin->Render();
        
        vtkNew<vtkWindowToImageFilter> imgf;
        imgf->SetInput(renWin);
        imgf->SetInputBufferTypeToRGB();
        imgf->Update();
        
        vtkNew<vtkPNGWriter> wr;
        wr->SetFileName(OPpath.c_str());
        wr->SetInputConnection(imgf->GetOutputPort());
        wr->Write();
        
}

int main() {

        auto renWin = vtkSmartPointer<vtkRenderWindow>::New();
        renWin->OffScreenRenderingOn();
        std::string test_dir = "./imgDump";
        
        for (size_t ii=0; ii<1500; ii++) {
                std::stringstream ssPath;
                ssPath << test_dir << "/sphere_" << ii << ".png";
                std::cout << ssPath.str() << std::endl;R
                testVTKDataViz(renWin, ssPath.str());
                renWin->Finalize();
        }
        
        return EXIT_SUCCESS;
}
1 Like

If you look into the code of vtkWindowToImageFilter’s SetInput() method:

void vtkWindowToImageFilter::SetInput(vtkWindow* input)
{
  if (input != this->Input)
  {
    if (this->Input)
    {
      this->Input->UnRegister(this);
    }
    this->Input = input;
    if (this->Input)
    {
      this->Input->Register(this);
    }
    this->Modified();
  }
}

Notice the vtkObjectBase::Register() method which, in the end, prevents the instance from being deallocated once its smart pointer goes out of the scope, hence the “memory leak”. You need to call imgf->SetInput(nullptr) to un-register the instance with the renWin object before leaving the scope of testVTKDataViz(). The way your test is coded, getting that VTK warning is expected.

Thanks for the suggestion. I tried it and am still having the same issue. Sequential calls to imgf->GetInput() before and after imgf->SetInput(nullptr) result in the following:

0x7faaebc04080
0x0

This seemingly confirms the vtkRenderWindow has been released by the vtkWindowToImageFilter. However, the memory problem remains. For good measure I tried also to SetInputConnection(nullptr) on the vtkPNGWriter, but no effect.

Make sure you pass the null pointers while still inside the testVTKDataViz() function:

void testVTKDataViz(ER
    vtkSmartPointer<vtkRenderWindow> renWin,
    std::string OPpath) {
        
        renWin->Render();
        
        vtkNew<vtkWindowToImageFilter> imgf;
        imgf->SetInput(renWin);
        imgf->SetInputBufferTypeToRGB();
        imgf->Update();
        
        vtkNew<vtkPNGWriter> wr;
        wr->SetFileName(OPpath.c_str());
        wr->SetInputConnection(imgf->GetOutputPort());
        wr->Write();

        imgf->SetInput(nullptr);
        wr->SetInputConnection(nullptr);
        
}

Yes, they’re inside the testVTKDataViz() function.

Since you already have a simple code that reproduces the issue, please, report a bug here: https://gitlab.kitware.com/vtk/vtk/-/issues

I’m attempting to report it but keep getting flagged as spam. I’ll wait an hour or so then try again.

Ok, done.