Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about memory consumption. #2636

Closed
ghost opened this issue Jun 23, 2019 · 8 comments
Closed

Question about memory consumption. #2636

ghost opened this issue Jun 23, 2019 · 8 comments

Comments

@ghost
Copy link

ghost commented Jun 23, 2019

So lets say I run some imgui code like this

ImGui::Begin("Some Name");
// insert random imgui widgets and stuffs
ImGui::End();

But i only run this code temporarily and never run it again. Will the memory that was allocated for that window ever be freed and if so when does it get freed? I have a program that basically generates Window classes with random names and calls some imgui code then deletes these classes later but the same name can never be generated again so im getting memory being allocated (Although quite minimal usually like 50-100kb per window) for each named Window and it seemingly never gets freed. Is there a way to manually free memory for all windows ever created?

Im using the latest version of imgui with the glfw3 and opengl3 backends.

@ghost ghost closed this as completed Jun 23, 2019
@ghost ghost reopened this Jun 23, 2019
@ocornut
Copy link
Owner

ocornut commented Jun 23, 2019

Hello,

I have a program that basically generates Window classes with random names

The first question to dig into is why are you needing to do that?

It is correct that windows are currently never freed, though the average cost for a window is certainly not going to be 50-100k but much less, unless you also submit that amount worth of vertices to them?

We could devise a solution but only by understanding the reasoning being your use case so please detail it.

A trivial workaround at the moment is to recreate the imgui context. That will be mostly transparent to the user.

I would eventually like to provide a way to compact existing windows (resize all buffers down) and one to delete windows (either explicitely or based or some heuristic such as “not used since xx”.

@Folling
Copy link

Folling commented Jun 29, 2019

To add to this topic, I have a similar usecase, where the user is able to open windows for various aspects of the Application.
E.g. I have a list of things, and he can then go ahead and open as many windows for those things as he wants. The windows are stored in a GUI state and then drawn in a loop.
Does that mean any windows that aren't drawn anymore, won't ever be freed?

@ocornut
Copy link
Owner

ocornut commented Jun 29, 2019

Does that mean any windows that aren't drawn anymore, won't ever be freed?

That is correct, it is currently this way.
We could potentially change that but I would need a more rigorous explanation of how it is affecting you.

The main problem to solve is to decide under which criteria a window should be evicted. People may want to preserve eg the tree node open states.

Depending on how much pressure there is to actually free memory we could devise variety of solutions, one being to simply compact most of the unused memory but not all of it, or to keep on lightweight frozen representations of the treenode storage data (would need to work out how this would work with #2577).

@Folling
Copy link

Folling commented Jun 29, 2019

It isn't actually affecting me in a negative sense currently. I didn't even notice until I looked at this ticket. However I haven't tested how bad the memory consumption actually gets. I'll do that in a moment and append the results.

I'm currently porting an application from JavaFX to imgui. The general idea is to have many different types of data, and lists of them, which the user can open windows for.
An alternative approach would of course be, to only have one window which displays the data for whatever element is selected in the list, though I prefer to give the user the freedom to have as many windows as he'd like - especially once I introduce docking to the whole thing.

The current code looks somewhat like this:

renderMainWindow();
if(userWantsToOpenFooWindow()) {
    windowState.FooWindows.add(selectedFoo);
}
for(Foo foo : windowState.FooWindows) {
    renderFooWindow(foo);
}

And this I have for many windows, currently there's 6 different lists, containing different types of windows to be displayed.

Edit;
After opening about 50 windows and closing them again (closing just means removing them from windowState.FooWindows, the memory hardly changed at all.
I started at ~23.5mb, and now am at 24.1mb, part of that, of course, is just the normal data.
I should note: I'm using ImGui.NET.

ocornut added a commit that referenced this issue Jun 30, 2019
… e.g. ImDrawList of unused window GcFreeTransientWindowBuffers(), (#2636) Need to decide if we should enable it.
@ocornut
Copy link
Owner

ocornut commented Jun 30, 2019

Edit; After opening about 50 windows and closing them again (closing just means removing them from windowState.FooWindows, the memory hardly changed at all.
I started at ~23.5mb, and now am at 24.1mb, part of that, of course, is just the normal data.
I should note: I'm using ImGui.NET.

The base memory footprint of a window is about 1 KB + mostly storage for active ImDrawList, that itself it going to be much higher (20 bytes per vertex).

I have pushed in features/gc branch (see commit above) some code to clear transient buffers of window (which doesn't erase windows themselves but I think clearing the ImDrawList is the most important thing to do. Actually erasing windows could be done as a second step)

The code is currently disabled, e.g.

// Garbage collect (this is totally functional but we may need decide if the side-effects are desirable)
//if (!window->WasActive && window->LastFrameActive == g.FrameCount - 1000)
//    GcFreeTransientWindowBuffers(window);

I would like feedback there because even though it works, it may puts additional allocation pressure if windows are often disabled and recreated. I suppose I could expose the framecount/time threshold as a configuration option so it could easily be disabled?

@Folling
Copy link

Folling commented Jun 30, 2019

I'm uncertain how exactly windows are stored internally, but at least for my usecase a function which can explicitly free one window given its id would be sufficient.

Something along the lines of:

ImGui::Begin("Foo Window");
RenderWindow();
ImGui::End();

// some more code

ImGui::FreeWindowResources("Foo Window");

Now, this is of course a bit harder with randomised window names as is the case with @frostfortune's usecase, however he could just use the differentiation between window name and window id and store a map from window name to window ID, given that no name can appear twice.

@ocornut
Copy link
Owner

ocornut commented Jun 30, 2019

We can of course provide that function. But the function will undoubtably be the cause of subtle bugs (we will need to chase all ImGuiWindow* pointers) so I would rather have it be exercised more agressively than just a manual call.

ocornut added a commit that referenced this issue Jul 28, 2019
… e.g. ImDrawList of unused window GcFreeTransientWindowBuffers(), (#2636) Need to decide if we should enable it.
ocornut added a commit that referenced this issue Aug 28, 2019
…ndows (buffers are compacted when a window is unused for 60 seconds, as per io.ConfigWindowsMemoryCompactTimer = 60.0f). Note that memory usage has never been reported as a problem, so this is merely a touch of overzealous luxury. (#2636)
ocornut added a commit that referenced this issue Aug 28, 2019
…ndows (buffers are compacted when a window is unused for 60 seconds, as per io.ConfigWindowsMemoryCompactTimer = 60.0f). Note that memory usage has never been reported as a problem, so this is merely a touch of overzealous luxury. (#2636)
@ocornut
Copy link
Owner

ocornut commented Aug 28, 2019

I have now pushed an improved version of this to master.

Default is to compact window buffers when unused for 60 seconds, as per
io.ConfigWindowsMemoryCompactTimer = 60.0

You can set to -1.0f to disable the feature, or temporarily 0.0f if you want to request a full compaction in a given frame for some reasonm.

Note that 95%+ of the memory cost are vertices/indices so this is a nice feature.

@ocornut ocornut closed this as completed Aug 28, 2019
corentin-plouet added a commit to corentin-plouet/imgui that referenced this issue Sep 22, 2019