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

105.1.1-2577 leaks RAM if run minimized and with /debug on #1661

Open
lhog opened this issue Aug 27, 2024 · 3 comments
Open

105.1.1-2577 leaks RAM if run minimized and with /debug on #1661

lhog opened this issue Aug 27, 2024 · 3 comments

Comments

@lhog
Copy link
Collaborator

lhog commented Aug 27, 2024

The leaked memory is reclaimed once the window is restored.

@saurtron
Copy link
Collaborator

saurtron commented Oct 28, 2024

Hi, I have investigated this a bit, and found a couple things.

Broken "draw 2 frames per minute when minimized"

See Game->Draw()

The check is (currentTimePreDraw - lastDrawFrameTime).toSecsi() < 30, but both variables are actually updated at the same time, since they're both at the top of the function, thus the test is always true, so the engine is never drawing when minimized (afaics).

See how lastDrawFrameTime is updated inside UpdateUnsynced() and currentTimePreDraw after that a bit above the aforementioned test.

As far as I can see the test was introduced at 034f019, then broken at a221856 (one month later) when updating of lastDrawFrameTime was moved to UpdateUnsynced(), thus going from the end of Draw() to the beginning.

So I think this has basically never worked, at least not after 2013.

This can be a general cause of leaking when minimized, since I believe the idea is Update might be accumulating data, while Draw is consuming it.

I can't say how many modules have this pattern. But it's likely there's more than one.

ProfileDrawer accumulating data.

I found one specific instance of this pattern, related to this issue, in ProfileDrawer. Here DbgTimingInfo is getting called from different places in engine, and accumulating a number of buffers with start/end times of different processes. Also TimerProfiler accumulates data on threadProfiles that only gets cleaned up by the ProfileDrawer.

These are consumed and cleaned up inside ProfileDrawer::DrawScreen->DrawFrameBarcode->DrawTimeSlices, where also, before processing, any data older than 0.5 secs (MAX_THREAD_HIST_TIME) is discarded.

So when Draw is not called data just keeps accumulating.

What to do

Regarding the lastDrawFrameTime, I believe updating this can be moved back to the end of Game->Draw.

Inside UpdateUnsynced lastDrawFrameTime is used to calculate deltaDrawFrameTime, just before setting it to current_time. I think globalRendering->lastFrameStart could be used here instead, thus liberating lastDrawFrameTime to just keep track of when the last Draw call happened.

See #1741 for a proposed fix.

Regarding the ProfileDrawer, the simple fix is moving the old data cleaning now done inside DrawTimeSlices to Update(). This way we will ensure to never accumulate more than the latest 0.5 seconds of data, even if draw is never called because of whatever reason.

Alternatively we could just not accumulate data if minimized, since we're not drawing anyways. Otherwise another possibility is just ignore this and just fix the issue above with the "2 frames per minute when minimized".

See #1742 for a proposed fix.

@lhog
Copy link
Collaborator Author

lhog commented Oct 28, 2024

Good job investigating this one!
I'll try to have a look during the week.

@saurtron
Copy link
Collaborator

After reviewing a bit more, I seen TimeProfiler also can leak data beyond what I already described here, see #1742 for detailed information about that and proposed fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants