projectM-visualizer / projectM-visualizer/projectm
[DEV BUG] Memory leak in libprojectM 4.1.6 renderer arrays during manual playlist switches on Windows
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 4.5k
- Forks
- 485
- Avg merge
- 3d 7h
- Merged PRs (30d)
- 5
Description
Please confirm the following points:
- This report is NOT about the Android apps in the Play Store
- I have searched the project page to check if the issue was already reported
Affected Project
libprojectM (including the playlist library)
Affected Version
4.1.6
Operating Systems and Architectures
Windows (x64)
Build Tools
Compiler: Microsoft Windows SDK
Additional Project, OS and Toolset Details
No response
Type of Defect
Specific bug in projectM code (please link the code in question)
Log Output
Describe the Issue
Summary
Memory leak identified in libprojectM 4.1.6 during continuous playlist-driven preset transitions on Windows. Memory usage climbs linearly from an 80MB baseline to over 1.2GB within a 7-8 hour window.
Environment
Library Version: libprojectM v4.1.6 Core Release
Operating System: Windows 11 x64
IDE / Toolchain: Visual Studio 2026 / MSVC Compiler
How to Reproduce
The leak occurs when disabling projectM's internal auto-transitioning and driving preset switches authoritatively from the application layer using a Win32 timer:
Instantiate the engine and immediately lock preset switching:
projectm_set_preset_locked(m_pProjectM, true);
Set up a standard Win32 WM_TIMER callback.
Every interval (e.g., matching preset durations), manually call:
projectm_playlist_play_next(m_pProjectM, false);
Observe memory usage over time via Task Manager or Diagnostic Tools.
Visual Studio Diagnostic Profiler Evidence
A heap snapshot differential taken between preset transition cycles reveals that while parent container structures are torn down, primitive array allocations inside the renderer namespace accumulate indefinitely on the process heap.
Here is the raw data dump from the Visual Studio 2026 Memory Profiler Diff (sorted by size impact):
void 4094 93016
projectM-4d.dll!libprojectM::Renderer::Color[] 10 84811
projectM-4d.dll!libprojectM::Renderer::Point[] 18 52042
char[] 172 20327
unsigned int[] 4 16007
projectM-4d.dll!libprojectM::MilkdropPreset::MilkdropPreset 1 15632
projectM-4d.dll!M4::HLSLTree::NodePage 2 8208
projectM-4d.dll!libprojectM::Renderer::TextureUV[] 3 6255
projectM-4d.dll!libprojectM::MilkdropPreset::CustomShape 4 5920
projectM-4d.dll!libprojectM::MilkdropPreset::CustomWaveform 4 5440
projectM-4d.dll!std::_Container_proxy 173 2768
projectM-4d.dll!libprojectM::MilkdropPreset::MilkdropShader 2 1840
char *[] 1 1464
projectM-4d.dll!std::_Ref_count_obj2libprojectM::Renderer::Texture 11 1056
projectM-4d.dll!M4::HLSLMacro *[] 1 736
projectM-4d.dll!std::_Tree_node<std::pair<unsigned int const ,std::shared_ptrlibprojectM::Renderer::TextureAttachment >,void *> 8 448
projectM-4d.dll!std::_Tree_node<std::pair<int const ,std::map<unsigned int,std::shared_ptrlibprojectM::Renderer::TextureAttachment,std::less,std::allocator<std::pair<unsigned int const ,std::shared_ptrlibprojectM::Renderer::TextureAttachment > > > >,void *> 7 448
projectM-4d.dll!std::_Tree_node<std::basic_string<char,std::char_traits,std::allocator >,void *> 4 288
projectM-4d.dll!std::_Ref_count_obj2libprojectM::Renderer::TextureAttachment 5 280
projectM-4d.dll!libprojectM::Renderer::TextureSamplerDescriptor 2 240
projectM-4d.dll!std::_Tree_node<std::pair<int const ,libprojectM::Renderer::TextureSamplerDescriptor>,void *> 1 160
projectM-4d.dll!std::_Tree_node<std::pair<M4::matrixCtor const ,std::basic_string<char,std::char_traits,std::allocator > >,void *> 1 112
projectM-4d.dll!std::_Ref_count_obj2libprojectM::Renderer::Sampler 2 64
bool *[] 1 64
projectM-4-playlistd.dll!std::_List_node<unsigned int,void *> 1 24
projectM-4-playlistd.dll!std::_Container_proxy 1 16
bool[] 1 16
unsigned int 2 8
char 1 1
Technical Observation
The numbers indicate a clear structural correlation. For every single new MilkdropPreset initialized, the system accumulates a matching set of CustomShape (+4) and CustomWaveform (+4) allocations.
Crucially, the raw vertex-drawing arrays associated with them (Color[] and Point[]) are left abandoned on the heap with positive count deltas. This confirms that while the parent objects are tracked, their low-level heap arrays lack a matching delete[] invocation during the transition sequence.
Technical Analysis
Since ~CustomShape() inside CustomShape.hpp is properly declared as a defaulted virtual destructor (virtual ~CustomShape() = default;), the parent wrapper object drops correctly.
However, the raw coordinate and color arrays (Color[], Point[]) used to instantiate the meshes for waveforms and shape elements are persisting on the system heap. This suggests that during a manual track change, the rendering pipeline misses an explicit array deallocation step (delete[]) or drops pointers before freeing the underlying memory boundaries of the active shader canvas elements.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the manual transition sequence using projectm_set_preset_locked and projectm_playlist_play_next, then inspect CustomShape.hpp and the renderer ownership of Color[] and Point[] allocations during preset teardown. Reproduce repeated switches on Windows with the Visual Studio Memory Profiler. Done means the reported arrays no longer accumulate across transitions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- computer-graphics, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 45/100