-
Bug
-
Resolution: Unresolved
-
None
-
1.15.1, 1.15.2 Pre-release 2, 1.16 Pre-release 4, 1.16.1, 20w28a, 20w29a, 20w30a, 1.16.2 Pre-release 1, 1.16.2, 1.16.3, 1.16.4 Pre-release 1, 1.16.4, 20w46a, 20w49a, 21w03a, 1.17.1, 21w39a, 21w44a, 1.20.1, 1.21
-
Community Consensus
-
Performance, Rendering
-
Important
-
Platform
The issue/symptoms:
When exploring terrain in MC, after first loading the world (or changing render distance) and watching VRAM/graphics memory usage, it first grows quite quickly, reaching hundreds of megabytes even with moderate render distance (on my nvidia geforce GT 740M GPU on linux, 600MB used by MC itself can be reached in under 15 minutes of flying around the world on render distance 12 chunks). Eventually VRAM usage stabilizes on an excessively large value, and doesn't drop until render distance is changed. After changing render distance, the process starts all over again. While this is largely not an issue when everything fits into GPU memory, it can cause issues when it doesn't. I first ran into this bug on 1.12.2, but later confirmed it on 1.15.1 and 1.15.2-pre2, and created a proof of concept workaround/fix as a Forge mod. On linux, I used nvidia-smi command to view GPU memory usage, but there should be tools for that on other systems.
Cause in the code:
When setting render distance, Minecraft reserves VBOs for all the 16x16x16 sections that exist within the render distance, without any data uploaded. From there, no new VBOs for chunks are created until render distance is changed again, and only the section positions assigned to them are changed.
When Minecraft rebuilds VBO data for a section, empty sections don't have any data assigned to them - for those, Minecraft skips uploading data.
When changing position assigned to a section, the old VBO, with all it's uploaded data intact, is kept around, until new data is uploaded. This works for cases when new data is actually uploaded, but when the new section has no data, this never happens, so the old data is kept around forever (until render distance is changed, or a different section with blocks in it takes it's place).
This means, that over time, as more terrain is explored, more of the section VBOs within view distance won't be rendered but will have garbage old data uploaded to them anyway. This can waste hundreds of megabytes of VRAM even on moderate view distance.
My workaround/fix for testing:
To test that my analysis is actually correct, I created the following mixin class on 1.15.1 Forge (the idea of it should be easy to understand even without understanding how mixin works - call to stopCompileTask in setPosition is redirected into this method, code uses MCP names):
@Mixin(ChunkRenderDispatcher.ChunkRender.class) public abstract class MixinRenderChunk { @Shadow public abstract void deleteGlResources(); @Shadow @Final private Map<RenderType, VertexBuffer> vertexBuffers; @Redirect(method = "setPosition", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$ChunkRender;stopCompileTask()V")) public void onSetPos(ChunkRenderDispatcher.ChunkRender chunkRender) { deleteGlResources(); RenderType.func_228661_n_().forEach(t -> vertexBuffers.put(t, new VertexBuffer(DefaultVertexFormats.BLOCK))); } }
I am aware that this is not a good, fast fix and causes noticeable performance issues, but it shows what the VRAM usage should be and is very easy to implement for testing purposes.
With render distance of 12 chunks, without the fix I would quickly accumulate over 600MB of graphics memory usage. With this fix, I was barely going above 150MB in most situations, and the memory usage was actually dropping when I moved away into an area with very few chunks rendered (just for comparison, 60MB of it was already used in main menu).