and added the OnResize function to myVTKApp class:
void myVTKApp::OnResize(unsigned int width, unsigned int height)
{
this->renWin->SetSize(width, height);
}
It simply assign a new size to the vtkRenderWindow.
Resizing then works as expected, but each time I resize the window the process memory increases by an incredibly huge amount. On my machine the process starts at 33.528 K (Private memory) and after a few resizing It reaches a few GB and eventually it will consume all the available memory and break.
Since I need to implement the resize behavior for the application I am working on, can anyone explain to me what is wrong here?
Here is what I see using a profiler. It seems that while resizing the window there is a huge allocation of strings. This is the stack for a string allocation:
My approach to those kind of mysterious memory leaks, especially with low level calls like that, is to reduce the program to a trivial case (e.g. empty void main()), build, run, observe, add a bit of code, build, run, observe, etc. Until you trigger the misbehavior. Either that or use something like Valgrind’s Memcheck (https://valgrind.org/docs/manual/mc-manual.html). I think a profiler won’t help you much to pinpoint the memory leak.
Thanks Paulo, I fully understand your approach to solve this type of problem. I did not write that the first time that I encountered the problem it was in a much more complex solution involving communication between gRPC processes etc. However as you suggested, I reduced even more the code from the VTK-src\Examples\GUI\Win32\SimpleCxx to this:
myVTKApp::myVTKApp(HWND hwnd)
{
// Similar to Examples/Tutorial/Step1/Cxx/Cone.cxx
// We create the basic parts of a pipeline and connect them
this->renderer = vtkRenderer::New();
this->renWin = vtkRenderWindow::New();
this->renWin->AddRenderer(this->renderer);
// setup the parent window
this->renWin->SetParentId(hwnd);
// Finally we start the interactor so that event will be handled
this->renWin->Render(); // this line triggers the memory leak. Without it there is no memory leak while resizing
}
The other modifications I did to the SimpleCxx is the code for resizing:
void myVTKApp::OnResize(unsigned int width, unsigned int height)
{
this->renWin->SetSize(width, height);
}
I guess the resizing code must be wrong, I am looking into it. Or maybe something in my VTK build or configuration. I forgot to mention that I am using mesa 3D too.
I am on a Windows 10 x64 machine, If I am not wrong Valgrind works on Linux only.
One more info: an application made with phyton and vtk (using mesa 3D) behaves correctly on my machine: the resize of the rendering window does not cause any memory leak. So I exclude a problem with my GPU driver
I suspect that a redraw event (likely followed by a VTK render) is being triggered for each mouse move. The VTK rendering routine is possibly not designed for that. I suggest you to take a look at this: https://stackoverflow.com/questions/2165759/how-do-i-force-windows-not-to-redraw-anything-in-my-dialog-when-the-user-is-resi . The point is to make sure there is just one VTK render only when the user finishes the resize, not while the window frame is being resized (e.g. during the mouse drag).
Hi Paulo, yes it definitely calls render while moving the mouse. I now have used the WM_EXITSIZEMOVE to resize only at the end of the mouse move. It partially solves the problem. Memory still gets consumed and with patience one could still break the program by resizing many times. For now I will use it this way. I wonder how the python vtk avoids this problem. I see that the python application does not leak any memory.
Python interface is a very high level API, so certainly there are lots of intemediary layers and libraries handling the gory details of lower level calls to the graphics system and the SO for you.
Your program is interfacing directly to Windows API, at the bottom end of the stack, so it is certainly missing a call, return value or message dispatch to trigger the de-allocation in VTK. Since I quit Windows API two decades ago (now I develop UIs with Qt), I don’t know the protocol Windows expects today.
Anyway, I suspect myVTKApp is being instantiated multiple times. Have you tried putting a printf() or something like this in the constructor? Can you show the part of your code where new myVTKApp(hWnd) is?
I see. You can check the code in my first message. It’s the Simple CX file taken from the VTK UI/Win32 example folder from the VTK installation. I just added the resize message handling as I described (eventually I called the UpdateSize function instead of SetSize but nothing changed). I can exclude that the constructor is called more than once. I have already debugged the program many times step by step. I also checked the MFC and python implementation from the VTK installation folders to get a clue. Made a few experiments still without success.
I’ve developed mainly for Windows, but GUI -wise mostly C# .Net. with native C++ for algorithms libraries or hardware interface
EDIT: here is the modified WndProc I use in the Win32Cone.Cxx:
With the profiler, debugging the VTK library I noticed that most of the allocation while resizing comes from the method vtkTextureObject::Resize(unsigned int width, unsigned int height) when the glTexImage2DMultisample is called:
In fact if I configure the vtkRenderWindow with SetMultiSamples(0) the memory leak disappears. This is not probably a solution given that the drawing degrades.
You can also try FXAA for antialiasing instead of MSAA. Maybe you can workaround the leak without compromising the aesthetics of your renderings. FXAA is also faster than MSAA, though some people don’t like its results.
Thank you Paulo,
I enabled the vtkDebugLeaks did some resizing and got this dump:
Class "vtkMinimalStandardRandomSequence" has 1 instance still around.
Class "vtkBoxMuellerRandomSequence" has 1 instance still around.
Class "vtkCellArray" has 1 instance still around.
Class "vtkPoints" has 4 instances still around.
Class "vtkPropCollection" has 5 instances still around.
Class "vtkMathInternal" has 1 instance still around.
Class "vtkOpenGLShaderCache" has 1 instance still around.
Class "vtkWorldPointPicker" has 1 instance still around.
Class "vtkInteractionStyleObjectFactory" has 1 instance still around.
Class "vtkIdTypeArray" has 1 instance still around.
Class "vtkInformation" has 99 instances still around.
Class "vtkTypeInt64Array" has 2 instances still around.
Class "vtkCollection" has 2 instances still around.
Class "vtkCommand or subclass" has 19 instances still around.
Class "vtkOpenGLRenderTimerLog" has 1 instance still around.
Class "class vtkBuffer<__int64>" has 3 instances still around.
Class "vtkUnsignedCharArray" has 2 instances still around.
Class "vtkObjectFactoryCollection" has 1 instance still around.
Class "vtkIdList" has 5 instances still around.
Class "vtkStereoCompositor" has 1 instance still around.
Class "vtkRenderingUIObjectFactory" has 1 instance still around.
Class "vtkRenderingFreeTypeObjectFactory" has 1 instance still around.
Class "vtkRenderingOpenGL2ObjectFactory" has 1 instance still around.
Class "vtkRendererCollection" has 1 instance still around.
Class "vtkActor2DCollection" has 1 instance still around.
Class "vtkLightCollection" has 1 instance still around.
Class "vtkOpenGLVertexArrayObject" has 42 instances still around.
Class "vtkActorCollection" has 3 instances still around.
Class "vtkVolumeCollection" has 1 instance still around.
Class "class vtkBuffer<double>" has 4 instances still around.
Class "vtkCullerCollection" has 1 instance still around.
Class "vtkOpenGLVertexBufferObjectGroup" has 6 instances still around.
Class "vtkGenericCell" has 2 instances still around.
Class "vtkEmptyCell" has 2 instances still around.
Class "vtkFrustumCoverageCuller" has 1 instance still around.
Class "vtkFXAAOptions" has 1 instance still around.
Class "vtkOpenGLRenderer" has 1 instance still around.
Class "vtkMatrix3x3" has 7 instances still around.
Class "vtkInformationIterator" has 12 instances still around.
Class "class vtkBuffer<unsigned char>" has 2 instances still around.
Class "vtkOutlineSource" has 6 instances still around.
Class "vtkCellPicker" has 2 instances still around.
Class "vtkInformationIntegerValue" has 72 instances still around.
Class "vtkOpenGLFramebufferObject" has 1 instance still around.
Class "vtkWin32OpenGLRenderWindow" has 1 instance still around.
Class "vtkInformationVector" has 48 instances still around.
Class "vtkCompositeDataPipeline" has 12 instances still around.
Class "class vtkBuffer<float>" has 2 instances still around.
Class "vtkTimerLog" has 6 instances still around.
Class "vtkOpenGLCellToVTKCellMap" has 6 instances still around.
Class "vtkOpenGLIndexBufferObject" has 42 instances still around.
Class "vtkMatrix4x4" has 39 instances still around.
Class "vtkTransform" has 15 instances still around.
Class "vtkTDxInteractorStyleSettings" has 4 instances still around.
Class "vtkFloatArray" has 2 instances still around.
Class "vtkOpenGLPolyDataMapper" has 6 instances still around.
Class "vtkAlgorithmOutput" has 6 instances still around.
Class "vtkPropPicker" has 1 instance still around.
Class "vtkInformationExecutivePortValue" has 6 instances still around.
Class "vtkInformationExecutivePortVectorValue" has 6 instances still around.
Class "vtkTDxInteractorStyleCamera" has 4 instances still around.
Class "vtkDoubleArray" has 4 instances still around.
Class "vtkProp3DCollection" has 2 instances still around.
Class "vtkInteractorStyleJoystickActor" has 1 instance still around.
Class "vtkInteractorStyleJoystickCamera" has 1 instance still around.
Class "vtkInteractorStyleTrackballActor" has 1 instance still around.
Class "vtkInteractorStyleTrackballCamera" has 1 instance still around.
Class "vtkInteractorStyleMultiTouchCamera" has 1 instance still around.
Class "vtkInteractorStyleSwitch" has 1 instance still around.
Class "vtkPickingManager" has 1 instance still around.
Class "vtkWin32RenderWindowInteractor" has 1 instance still around.
Class "vtkOpenGLVertexBufferObjectCache" has 1 instance still around.
Class "vtkTextureUnitManager" has 1 instance still around.
Class "vtkOpenGLState" has 1 instance still around.
Class "vtkTextureObject" has 2 instances still around.
Class "vtkPerspectiveTransform" has 2 instances still around.
Class "vtkSimpleTransform" has 4 instances still around.
Class "vtkOpenGLCamera" has 1 instance still around.
Class "vtkOpenGLLight" has 1 instance still around.
However I don’t know if those are true memory leaks. I called vtkDebugLeaks::PrintCurrentLeaks(); just before setting m_renderer->SetRenderWindow(nullptr); in my main class destructor.
All the class members are vtkSmartPointer or std::unique_ptr:
From that dump I found nothing abnormal. Those instance counts are within the expected. Did you get that dump after resizing a number of times? You know, to see whether there is any count increasing abnormally.
Then I suspect the OpenGL side or even on the graphics card’s driver is causing the problem. Make sure you have the latest drivers. You said before that other 3D applications work fine, ruling out something in the drivers, but they don’t necessarily result in exactly the same OpenGL calls. Either that or you’re using some older VTK version.
I see. I have built the latest stable 9.01. The python application could use an older version I will try to find out. Perhaps the VTK initialization in python is different, the multisample setting or some other setting. I tried to find the python code without success till now. I know that the application was built with Mayavi, a scientific data visualizer written in Python which uses VTK. The memory leak is shown on another windows machine. Both machines have AMD Radeon graphic card, though.
I have a concern: while rebuilding the VTK to use the vtkDebugLeaks, I set the preprocessor definition VTK_DEBUG_LEAKS only in projects CommonCore and RenderingOpenGL2. Is it enough?
Thanks a lot for your time
Or better, for all modules whose libvtk*.DLL's your program requires. You can use something like Dependency Walker to find runtime dependencies in Windows.
Class "vtkMinimalStandardRandomSequence" has 1 instance still around.
Class "vtkCellArray" has 1 instance still around.
Class "vtkWin32OutputWindow" has 1 instance still around.
Class "class vtkBuffer<__int64>" has 3 instances still around.
Class "vtkBoxMuellerRandomSequence" has 1 instance still around.
Class "vtkMathInternal" has 1 instance still around.
Class "vtkIdTypeArray" has 1 instance still around.
Class "vtkTypeInt64Array" has 2 instances still around.
Class "vtkObjectFactoryCollection" has 1 instance still around.
Class "vtkIdList" has 1 instance still around.
Class "vtkRenderingUIObjectFactory" has 1 instance still around.
Class "vtkRenderingOpenGL2ObjectFactory" has 1 instance still around.
This time the dump is created after destroying all vtk objects in destructor:
I don’t see how this dump might justify what I see in the profiler:
1- taken at startup
2- taken after vtk first cone rendering
3- taken after some resizing (about 100)
4- taken before calling PrintCurrentLeaks() in destructor
This is the memory dump taken before calling PrintCurrentLeaks() (so that heap should likely be free of most of vtk objects). These are the first lines but there are more. I see a different result from the vtkDebugLeaks (many more vtk objects alive):
EDIT:
comparing to the case when Multi sample is set to 0 and no memory leaks. ------------------
vtkDebugLeaks Dump:
Class "vtkMinimalStandardRandomSequence" has 1 instance still around.
Class "class vtkBuffer<__int64>" has 3 instances still around.
Class "vtkBoxMuellerRandomSequence" has 1 instance still around.
Class "vtkMathInternal" has 1 instance still around.
Class "vtkIdTypeArray" has 1 instance still around.
Class "vtkTypeInt64Array" has 2 instances still around.
Class "vtkIdList" has 1 instance still around.
Class "vtkCellArray" has 1 instance still around.
Class "vtkRenderingUIObjectFactory" has 1 instance still around.
Class "vtkObjectFactoryCollection" has 1 instance still around.
Class "vtkRenderingOpenGL2ObjectFactory" has 1 instance still around.
Class "vtkWin32OutputWindow" has 1 instance still around.
Same output as before (just some entries in different position).
Heap allocation (taken at the same steps as before):
Memory dump at the end (as expected many fewer objects):
I don’t know which version of mayavi and vtk the working application uses.
The Mayavi Scene control doesn’t seem to do anything fancy. Excerpt from the create control routine:
def _create_control(self, parent):
// Create the toolkit-specific control that represents the widget.
if self.off_screen_rendering:
if hasattr(tvtk, 'EGLRenderWindow'):
renwin = tvtk.EGLRenderWindow()
elif hasattr(tvtk, 'OSOpenGLRenderWindow'):
renwin = tvtk.OSOpenGLRenderWindow()
else:
renwin = tvtk.RenderWindow()
# If we are doing offscreen rendering we set the window size to
# (1,1) so the window does not appear at all
renwin.size = (1, 1)
self._renwin = renwin
self._interactor = tvtk.GenericRenderWindowInteractor(
render_window=renwin
)
else:
renwin = self._renwin = tvtk.RenderWindow()
self._interactor = tvtk.RenderWindowInteractor(
render_window=renwin
)
renwin.trait_set(point_smoothing=self.point_smoothing,
line_smoothing=self.line_smoothing,
polygon_smoothing=self.polygon_smoothing)
# Create a renderer and add it to the renderwindow
self._renderer = tvtk.Renderer()
renwin.add_renderer(self._renderer)
# Save a reference to our camera so it is not GC'd -- needed for
# the sync_traits to work.
self._camera = self.camera
# Sync various traits.
self._renderer.background = self.background
self.sync_trait('background', self._renderer)
self._renderer.on_trait_change(self.render, 'background')
self._camera.parallel_projection = self.parallel_projection
self.sync_trait('parallel_projection', self._camera)
renwin.off_screen_rendering = self.off_screen_rendering
self.sync_trait('off_screen_rendering', self._renwin)
self.render_window.on_trait_change(self.render, 'off_screen_rendering')
self.render_window.on_trait_change(self.render, 'stereo_render')
self.render_window.on_trait_change(self.render, 'stereo_type')
self.camera.on_trait_change(self.render, 'parallel_projection')
self._interactor.initialize()
self._interactor.render()
self.light_manager = light_manager.LightManager(self)
if self.off_screen_rendering:
# We want the default size to be the normal (300, 300).
# Setting the size now should not resize the window if
# offscreen is working properly in VTK.
renwin.size = (300, 300)
return self._interactor
Set size routine:
def set_size(self, size):
// Set the size of the window.
self._interactor.size = size
self._renwin.size
The leak is somewhere else by the looks of it. vtkDebugLeaks only monitors the garbage collection of VTK objects. Your OpenGL back end (driver) may be causing it and it won’t show up in the dump.
MSAA is done in the graphics card. Your driver may be to blame still.
I insist: make sure you have the latest graphics card driver installed.