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

Memory bloat/leak when using VertexBuffer::slice() (sometimes up to 500MB!) #2001

Open
KeyboardDanni opened this issue Mar 24, 2022 · 4 comments

Comments

@KeyboardDanni
Copy link
Contributor

Windows 10 x64 (though I've noticed this in the past on Linux)
nVidia GeForce GTX 1060 6GB
nVidia Driver version 460.79
Using OpenGL 3.3 Core context

How to reproduce: replace examples/instancing.rs with this: https://gist.github.com/cosmicchipsocket/0ca82f61867b226e59d65e4e6c8f3aad

To make the above go faster, replace line 40 of examples/support/mod.rs with next_frame_time = Instant::now();

The above example is quite mild (memory bloat is only about ~20MB) but in my application, where I rotate between many instance buffers, the memory bloat is severe, in some cases ballooning up by hundreds of megabytes. My best guess from the heap profiling is that glium is somehow storing every single possible start/end slice combination for a buffer into a hashmap, and allocating OpenGL resources along with that. This is unworkable for me - I am trying to minimize buffer writes so I am writing multiple draw batches to one buffer and then issuing separate draws for each portion of the buffer. Worst case scenario this gives over 2x performance improvement for lots of batch breaks. I also need multiple buffers so that the pipeline doesn't stall waiting for in-use buffers to finish drawing operations before re-writing their contents.

Is there any way that this memory use can be kept under control?

@KeyboardDanni
Copy link
Contributor Author

Yep, the VAO cache appears to be the culprit. Drawing 10k sprites by slicing the buffer is enough to fill it up with over 100k entries. I noticed that the hashmap in the cache has the buffer offset as part of the key. I removed this from the key and the bloat is gone (but sprites no longer draw correctly).

Is there any chance we could introduce a better slice mechanism, maybe a draw() counterpart that takes a range that is then passed directly to OpenGL (after being checked for safety) instead of added as yet another entry to the VAO cache?

@KeyboardDanni
Copy link
Contributor Author

An update: So I was able to mitigate this issue somewhat - I rewrote my batching system to use a series of "batch runs" on the buffer, and each new batch run always uses a multiple of some constant like 128 as the starting index. This combined with reducing the number of buffers I rotate through from 32 down to 4 has reduced the VAO count considerably - now only 64 VAOs are created in the VAO cache so memory usage only increases by about a megabyte. Much better!

Still I'm wondering if there might be a way to redesign the VAO cache to cut down on this further (since another 64 VAOs will be created for each mesh + shader combination that I might use). It doesn't help that the online documentation I've seen doesn't seem to go into detail on what exact state is contained within a VAO and under which circumstances a VAO can be repurposed for, say, a different buffer, or a part of the same buffer.

Leaving this open in case someone finds an answer.

@est31
Copy link
Collaborator

est31 commented Mar 26, 2022

PRs to improve this use case are welcome (at least ones that don't hurt the average case), e.g. some function to purge the cache? IDK.

@KeyboardDanni
Copy link
Contributor Author

There is in fact a function to purge the cache, but it's in a private module (vertex_array_object.rs). It does seem to automatically fire whenever a vertex buffer is dropped.

I was thinking more along the lines of whether the VAO cache would need to have so many entries and whether some of those VAO could be reused. But it does seem like a risky change for a library that's currently in maintenance mode.

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