1818package net .raphimc .immediatelyfast .feature .core ;
1919
2020import com .mojang .blaze3d .systems .RenderSystem ;
21- import it .unimi .dsi .fastutil .objects .Reference2LongMap ;
22- import it .unimi .dsi .fastutil .objects .Reference2LongOpenHashMap ;
21+ import it .unimi .dsi .fastutil .objects .Reference2ObjectMap ;
22+ import it .unimi .dsi .fastutil .objects .Reference2ObjectOpenHashMap ;
2323import it .unimi .dsi .fastutil .objects .ReferenceArrayList ;
2424import it .unimi .dsi .fastutil .objects .ReferenceList ;
2525import net .minecraft .client .util .BufferAllocator ;
2626import net .raphimc .immediatelyfast .ImmediatelyFast ;
2727
2828public class BufferAllocatorPool {
2929
30- private static final ReferenceList <BufferAllocator > FREE = new ReferenceArrayList <>();
31- private static final ReferenceList <BufferAllocator > IN_USE = new ReferenceArrayList <>();
32- private static final Reference2LongMap <BufferAllocator > BUFFER_ALLOCATOR_ACCESS_TIME = new Reference2LongOpenHashMap <>();
30+ private static final ReferenceList <Entry > FREE = new ReferenceArrayList <>();
31+ private static final ReferenceList <Entry > IN_USE = new ReferenceArrayList <>();
32+ private static final Reference2ObjectMap <BufferAllocator , Entry > BUFFER_ALLOCATOR_MAPPING = new Reference2ObjectOpenHashMap <>();
3333
3434 private BufferAllocatorPool () {
3535 }
3636
3737 public static BufferAllocator borrowBufferAllocator () {
3838 RenderSystem .assertOnRenderThread ();
39- BufferAllocator bufferAllocator ;
39+ Entry entry ;
4040 if (FREE .isEmpty ()) {
41- bufferAllocator = new BufferAllocator (256 );
41+ entry = new Entry ( new BufferAllocator (256 ) );
4242 } else {
43- bufferAllocator = FREE .removeFirst ();
44- if (bufferAllocator .pointer == 0L ) { // If the buffer was closed while in the pool
45- BUFFER_ALLOCATOR_ACCESS_TIME . removeLong ( bufferAllocator );
46- bufferAllocator = new BufferAllocator (256 );
43+ entry = FREE .removeFirst ();
44+ if (entry . bufferAllocator .pointer == 0L ) { // If the buffer was closed while in the pool
45+ BUFFER_ALLOCATOR_MAPPING . remove ( entry . bufferAllocator );
46+ entry = new Entry ( new BufferAllocator (256 ) );
4747 }
4848 }
49- IN_USE .add (bufferAllocator );
50- BUFFER_ALLOCATOR_ACCESS_TIME .put (bufferAllocator , System .currentTimeMillis ());
51- return bufferAllocator ;
49+ IN_USE .add (entry );
50+ BUFFER_ALLOCATOR_MAPPING .put (entry .bufferAllocator , entry );
51+ entry .onBorrow ();
52+ return entry .bufferAllocator ;
5253 }
5354
5455 public static void returnBufferAllocatorSafe (final BufferAllocator bufferAllocator ) {
5556 RenderSystem .assertOnRenderThread ();
56- if (!IN_USE .remove (bufferAllocator )) {
57+ final Entry entry = BUFFER_ALLOCATOR_MAPPING .get (bufferAllocator );
58+ if (!IN_USE .remove (entry )) {
5759 return ;
5860 }
59- bufferAllocator . reset ();
60- FREE .add ( bufferAllocator );
61+ entry . onReturn ();
62+ FREE .addFirst ( entry );
6163 }
6264
6365 public static int getSize () {
@@ -66,23 +68,59 @@ public static int getSize() {
6668
6769 public static void onEndFrame () {
6870 if (!IN_USE .isEmpty ()) {
69- ImmediatelyFast .LOGGER .warn (IN_USE .size () + " BufferAllocator(s) were not returned to the pool. Forcibly reclaiming them." );
70- for (BufferAllocator bufferAllocator : IN_USE ) {
71- bufferAllocator .reset ();
71+ // Reclaim all buffers that were not returned to the pool this and the last frame
72+ final boolean leak = IN_USE .removeIf (entry -> {
73+ if (entry .inUseOverMultipleFrames ) {
74+ entry .onReturn ();
75+ FREE .addFirst (entry );
76+ return true ;
77+ }
78+ return false ;
79+ });
80+ if (leak ) {
81+ ImmediatelyFast .LOGGER .warn ("Some BufferAllocators were not returned to the pool. Forcibly reclaiming them to prevent a memory leak." );
82+ }
83+
84+ // Mark all as in use over multiple frames
85+ for (Entry entry : IN_USE ) {
86+ entry .inUseOverMultipleFrames = true ;
7287 }
73- FREE .addAll (IN_USE );
74- IN_USE .clear ();
7588 }
76- BUFFER_ALLOCATOR_ACCESS_TIME .reference2LongEntrySet ().removeIf (entry -> {
77- if (System .currentTimeMillis () - entry .getLongValue () > 60 * 1000 ) {
78- if (FREE .contains (entry .getKey ())) {
79- FREE .remove (entry .getKey ());
80- entry .getKey ().close ();
81- }
89+
90+ FREE .removeIf (entry -> {
91+ if (entry .shouldBeClosed ()) {
92+ entry .bufferAllocator .close ();
93+ BUFFER_ALLOCATOR_MAPPING .remove (entry .bufferAllocator );
8294 return true ;
8395 }
8496 return false ;
8597 });
8698 }
8799
100+ private static class Entry {
101+
102+ private final BufferAllocator bufferAllocator ;
103+ private long lastAccessTime ;
104+ private boolean inUseOverMultipleFrames ;
105+
106+ public Entry (final BufferAllocator bufferAllocator ) {
107+ this .bufferAllocator = bufferAllocator ;
108+ this .lastAccessTime = System .currentTimeMillis ();
109+ }
110+
111+ public boolean shouldBeClosed () {
112+ return System .currentTimeMillis () - this .lastAccessTime > 60 * 1000 ;
113+ }
114+
115+ public void onBorrow () {
116+ this .lastAccessTime = System .currentTimeMillis ();
117+ }
118+
119+ public void onReturn () {
120+ this .bufferAllocator .reset ();
121+ this .inUseOverMultipleFrames = false ;
122+ }
123+
124+ }
125+
88126}
0 commit comments