Skip to content

Commit eb6700e

Browse files
committed
Merge remote-tracking branch 'origin/1.20' into 1.21.1
2 parents a287375 + fd46baa commit eb6700e

File tree

4 files changed

+197
-29
lines changed

4 files changed

+197
-29
lines changed
Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,29 @@
11
package org.embeddedt.modernfix.common.mixin.perf.dynamic_sounds;
22

3-
import com.google.common.cache.CacheBuilder;
4-
import com.google.common.cache.RemovalCause;
5-
import com.google.common.cache.RemovalNotification;
63
import com.mojang.blaze3d.audio.SoundBuffer;
74
import net.minecraft.client.sounds.SoundBufferLibrary;
85
import net.minecraft.resources.ResourceLocation;
96
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
107
import org.embeddedt.modernfix.dynamicresources.DynamicSoundHelpers;
11-
import org.embeddedt.modernfix.ModernFix;
128
import org.spongepowered.asm.mixin.Final;
139
import org.spongepowered.asm.mixin.Mixin;
1410
import org.spongepowered.asm.mixin.Mutable;
1511
import org.spongepowered.asm.mixin.Shadow;
12+
import org.spongepowered.asm.mixin.injection.At;
13+
import org.spongepowered.asm.mixin.injection.Inject;
14+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
1615

1716
import java.util.concurrent.CompletableFuture;
18-
import java.util.concurrent.TimeUnit;
1917
import java.util.Map;
2018

2119
@Mixin(SoundBufferLibrary.class)
2220
@ClientOnlyMixin
2321
public abstract class SoundBufferLibraryMixin {
24-
25-
private static final boolean debugDynamicSoundLoading = Boolean.getBoolean("modernfix.debugDynamicSoundLoading");
26-
2722
@Shadow @Final @Mutable
28-
private Map<ResourceLocation, CompletableFuture<SoundBuffer>> cache = CacheBuilder.newBuilder()
29-
.expireAfterAccess(DynamicSoundHelpers.MAX_SOUND_LIFETIME_SECS, TimeUnit.SECONDS)
30-
.concurrencyLevel(1)
31-
// Excessive use of type hinting due to it assuming Object as the broadest correct type
32-
.<ResourceLocation, CompletableFuture<SoundBuffer>>removalListener(this::onSoundRemoval)
33-
.build()
34-
.asMap();
23+
private Map<ResourceLocation, CompletableFuture<SoundBuffer>> cache;
3524

36-
private <K extends ResourceLocation, V extends CompletableFuture<SoundBuffer>> void onSoundRemoval(RemovalNotification<K, V> notification) {
37-
if(notification.getCause() == RemovalCause.REPLACED && notification.getValue() == cache.get(notification.getKey()))
38-
return;
39-
notification.getValue().thenAccept(SoundBuffer::discardAlBuffer);
40-
if(!debugDynamicSoundLoading)
41-
return;
42-
K k = notification.getKey();
43-
if(k == null)
44-
return;
45-
ModernFix.LOGGER.warn("Evicted sound {}", k);
25+
@Inject(method = "<init>", at = @At("RETURN"))
26+
private void replaceCache(CallbackInfo ci) {
27+
this.cache = new DynamicSoundHelpers.Cache(cache);
4628
}
4729
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.embeddedt.modernfix.common.mixin.perf.dynamic_sounds;
2+
3+
import com.mojang.blaze3d.audio.SoundBuffer;
4+
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
5+
import org.embeddedt.modernfix.dynamicresources.DynamicSoundHelpers;
6+
import org.spongepowered.asm.mixin.Mixin;
7+
import org.spongepowered.asm.mixin.Unique;
8+
import org.spongepowered.asm.mixin.injection.At;
9+
import org.spongepowered.asm.mixin.injection.Inject;
10+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
11+
12+
import javax.sound.sampled.AudioFormat;
13+
import java.nio.ByteBuffer;
14+
import java.util.concurrent.TimeUnit;
15+
16+
@ClientOnlyMixin
17+
@Mixin(SoundBuffer.class)
18+
public class SoundBufferMixin implements DynamicSoundHelpers.SoundBufAccess {
19+
@Unique
20+
private long mfix$durationNanos;
21+
22+
@Inject(method = "<init>", at = @At("RETURN"))
23+
private void computeDuration(ByteBuffer data, AudioFormat format, CallbackInfo ci) {
24+
if (data != null) {
25+
int numFrames = data.capacity() / format.getFrameSize();
26+
double seconds = ((double)numFrames / format.getFrameRate());
27+
mfix$durationNanos = Math.max(0, (long)Math.ceil(seconds * 1_000_000_000.0));
28+
} else {
29+
mfix$durationNanos = 0;
30+
}
31+
}
32+
33+
@Override
34+
public long mfix$getDurationNanos() {
35+
return mfix$durationNanos;
36+
}
37+
}
Lines changed: 138 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,142 @@
11
package org.embeddedt.modernfix.dynamicresources;
22

3+
import com.mojang.blaze3d.audio.SoundBuffer;
4+
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
5+
import net.minecraft.resources.ResourceLocation;
6+
import org.embeddedt.modernfix.ModernFix;
7+
import org.jetbrains.annotations.NotNull;
8+
9+
import java.util.AbstractMap;
10+
import java.util.AbstractSet;
11+
import java.util.Iterator;
12+
import java.util.Map;
13+
import java.util.Set;
14+
import java.util.concurrent.CompletableFuture;
15+
import java.util.concurrent.TimeUnit;
16+
317
public class DynamicSoundHelpers {
4-
/**
5-
* The duration until a sound is eligible for eviction if unused.
6-
*/
7-
public static final int MAX_SOUND_LIFETIME_SECS = 300;
18+
private static final long SOUND_EVICTION_DELAY = TimeUnit.SECONDS.toNanos(30);
19+
private static final boolean debugDynamicSoundLoading = Boolean.getBoolean("modernfix.debugDynamicSoundLoading");
20+
21+
public interface SoundBufAccess {
22+
long mfix$getDurationNanos();
23+
}
24+
25+
public static final class Cache extends AbstractMap<ResourceLocation, CompletableFuture<SoundBuffer>> {
26+
private static class Entry {
27+
private final CompletableFuture<SoundBuffer> buffer;
28+
private long lastAccessTime;
29+
30+
private Entry(CompletableFuture<SoundBuffer> buffer) {
31+
this.buffer = buffer;
32+
this.lastAccessTime = System.nanoTime();
33+
}
34+
35+
public CompletableFuture<SoundBuffer> getBuffer() {
36+
this.lastAccessTime = System.nanoTime();
37+
return this.buffer;
38+
}
39+
40+
public long getDuration() {
41+
var buf = this.buffer.getNow(null);
42+
if (buf == null) {
43+
return 0;
44+
}
45+
return ((SoundBufAccess)buf).mfix$getDurationNanos();
46+
}
47+
48+
public boolean isExpired(long currentTs) {
49+
long duration = getDuration();
50+
return duration > 0 && (currentTs - this.lastAccessTime) >= (duration + SOUND_EVICTION_DELAY);
51+
}
52+
53+
public void discard() {
54+
this.buffer.thenAccept(SoundBuffer::discardAlBuffer);
55+
}
56+
57+
@Override
58+
public String toString() {
59+
return super.toString();
60+
}
61+
}
62+
63+
private final Object2ObjectLinkedOpenHashMap<ResourceLocation, Entry> store = new Object2ObjectLinkedOpenHashMap<>();
64+
65+
public Cache(Map<ResourceLocation, CompletableFuture<SoundBuffer>> otherMap) {
66+
this.putAll(otherMap);
67+
}
68+
69+
private void checkExpired() {
70+
long ts = System.nanoTime();
71+
var iter = this.store.object2ObjectEntrySet().fastIterator();
72+
while (iter.hasNext()) {
73+
var entry = iter.next();
74+
if (entry.getValue().isExpired(ts)) {
75+
if (debugDynamicSoundLoading) {
76+
ModernFix.LOGGER.warn("Evicted sound {} with duration {} ms", entry.getKey(), entry.getValue().getDuration() / 1000000);
77+
}
78+
entry.getValue().discard();
79+
iter.remove();
80+
} else {
81+
break;
82+
}
83+
}
84+
}
85+
86+
@Override
87+
public CompletableFuture<SoundBuffer> get(Object key) {
88+
if (key instanceof ResourceLocation rl) {
89+
var entry = this.store.getAndMoveToLast(rl);
90+
CompletableFuture<SoundBuffer> result = entry != null ? entry.getBuffer() : null;
91+
this.checkExpired();
92+
return result;
93+
} else {
94+
return null;
95+
}
96+
}
97+
98+
@Override
99+
public CompletableFuture<SoundBuffer> put(ResourceLocation key, CompletableFuture<SoundBuffer> value) {
100+
var entry = new Entry(value);
101+
if (debugDynamicSoundLoading) {
102+
ModernFix.LOGGER.info("Loaded sound {}", key);
103+
}
104+
var previousEntry = this.store.putAndMoveToLast(key, entry);
105+
return previousEntry != null ? previousEntry.getBuffer() : null;
106+
}
107+
108+
@Override
109+
public @NotNull Set<Map.Entry<ResourceLocation, CompletableFuture<SoundBuffer>>> entrySet() {
110+
return new EntrySet();
111+
}
112+
113+
private class EntrySet extends AbstractSet<Map.Entry<ResourceLocation, CompletableFuture<SoundBuffer>>> {
114+
@Override
115+
public Iterator<Map.Entry<ResourceLocation, CompletableFuture<SoundBuffer>>> iterator() {
116+
var storeIter = store.entrySet().iterator();
117+
return new Iterator<>() {
118+
@Override
119+
public boolean hasNext() {
120+
return storeIter.hasNext();
121+
}
122+
123+
@Override
124+
public Map.Entry<ResourceLocation, CompletableFuture<SoundBuffer>> next() {
125+
var entry = storeIter.next();
126+
return new AbstractMap.SimpleImmutableEntry<>(entry.getKey(), entry.getValue().buffer);
127+
}
128+
};
129+
}
130+
131+
@Override
132+
public int size() {
133+
return store.size();
134+
}
135+
136+
@Override
137+
public void clear() {
138+
store.clear();
139+
}
140+
}
141+
}
8142
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.embeddedt.modernfix.sound;
2+
3+
import com.mojang.blaze3d.audio.SoundBuffer;
4+
import net.minecraft.resources.ResourceLocation;
5+
6+
import java.util.LinkedHashMap;
7+
import java.util.Map;
8+
import java.util.concurrent.CompletableFuture;
9+
10+
public class SoundBufferCache extends LinkedHashMap<ResourceLocation, CompletableFuture<SoundBuffer>> {
11+
@Override
12+
protected boolean removeEldestEntry(Map.Entry<ResourceLocation, CompletableFuture<SoundBuffer>> eldest) {
13+
return super.removeEldestEntry(eldest);
14+
}
15+
}

0 commit comments

Comments
 (0)