Fix chunk caching race condition add toggle

- /flywheel chunkCaching on|off
 - synchronized ftw
This commit is contained in:
Jozufozu 2021-08-02 00:06:26 -07:00
parent a5850a46c8
commit b530ea1be5
5 changed files with 69 additions and 12 deletions

View file

@ -43,6 +43,7 @@ public class Backend {
private Matrix4f projectionMatrix = new Matrix4f(); private Matrix4f projectionMatrix = new Matrix4f();
private boolean instancedArrays; private boolean instancedArrays;
private boolean enabled; private boolean enabled;
public boolean chunkCachingEnabled;
private final List<IShaderContext<?>> contexts = new ArrayList<>(); private final List<IShaderContext<?>> contexts = new ArrayList<>();
private final Map<ResourceLocation, MaterialSpec<?>> materialRegistry = new HashMap<>(); private final Map<ResourceLocation, MaterialSpec<?>> materialRegistry = new HashMap<>();
@ -153,6 +154,8 @@ public class Backend {
enabled = FlwConfig.get() enabled = FlwConfig.get()
.enabled() && !OptifineHandler.usingShaders(); .enabled() && !OptifineHandler.usingShaders();
chunkCachingEnabled = FlwConfig.get()
.chunkCaching();
} }
public boolean canUseInstancing(@Nullable World world) { public boolean canUseInstancing(@Nullable World world) {

View file

@ -18,6 +18,7 @@ import net.minecraftforge.api.distmarker.OnlyIn;
public enum BooleanConfig { public enum BooleanConfig {
ENGINE(() -> BooleanConfig::enabled), ENGINE(() -> BooleanConfig::enabled),
NORMAL_OVERLAY(() -> BooleanConfig::normalOverlay), NORMAL_OVERLAY(() -> BooleanConfig::normalOverlay),
CHUNK_CACHING(() -> BooleanConfig::chunkCaching),
; ;
final Supplier<Consumer<BooleanDirective>> receiver; final Supplier<Consumer<BooleanDirective>> receiver;
@ -71,6 +72,25 @@ public enum BooleanConfig {
player.displayClientMessage(text, false); player.displayClientMessage(text, false);
} }
@OnlyIn(Dist.CLIENT)
private static void chunkCaching(BooleanDirective state) {
ClientPlayerEntity player = Minecraft.getInstance().player;
if (player == null || state == null) return;
if (state == BooleanDirective.DISPLAY) {
ITextComponent text = new StringTextComponent("Chunk caching is currently: ").append(boolToText(FlwConfig.get().client.debugNormals.get()));
player.displayClientMessage(text, false);
return;
}
FlwConfig.get().client.chunkCaching.set(state.get());
ITextComponent text = boolToText(FlwConfig.get().client.chunkCaching.get()).append(new StringTextComponent(" chunk caching").withStyle(TextFormatting.WHITE));
player.displayClientMessage(text, false);
Backend.reloadWorldRenderers();
}
private static IFormattableTextComponent boolToText(boolean b) { private static IFormattableTextComponent boolToText(boolean b) {
return b ? new StringTextComponent("enabled").withStyle(TextFormatting.DARK_GREEN) : new StringTextComponent("disabled").withStyle(TextFormatting.RED); return b ? new StringTextComponent("enabled").withStyle(TextFormatting.DARK_GREEN) : new StringTextComponent("disabled").withStyle(TextFormatting.RED);
} }

View file

@ -16,6 +16,8 @@ public class FlwCommands {
dispatcher.register(Commands.literal("flywheel") dispatcher.register(Commands.literal("flywheel")
.then(new BooleanConfigCommand("backend", BooleanConfig.ENGINE).register()) .then(new BooleanConfigCommand("backend", BooleanConfig.ENGINE).register())
.then(new BooleanConfigCommand("debugNormals", BooleanConfig.NORMAL_OVERLAY).register())); .then(new BooleanConfigCommand("debugNormals", BooleanConfig.NORMAL_OVERLAY).register())
.then(new BooleanConfigCommand("chunkCaching", BooleanConfig.CHUNK_CACHING).register())
);
} }
} }

View file

@ -34,12 +34,17 @@ public class FlwConfig {
return client.debugNormals.get(); return client.debugNormals.get();
} }
public boolean chunkCaching() {
return client.chunkCaching.get();
}
public static void init() { public static void init() {
} }
public static class ClientConfig { public static class ClientConfig {
public final BooleanValue enabled; public final BooleanValue enabled;
public final BooleanValue debugNormals; public final BooleanValue debugNormals;
public final BooleanValue chunkCaching;
public ClientConfig(ForgeConfigSpec.Builder builder) { public ClientConfig(ForgeConfigSpec.Builder builder) {
@ -48,6 +53,9 @@ public class FlwConfig {
debugNormals = builder.comment("Enable or disable a debug overlay that colors pixels by their normal") debugNormals = builder.comment("Enable or disable a debug overlay that colors pixels by their normal")
.define("debugNormals", false); .define("debugNormals", false);
chunkCaching = builder.comment("Cache chunk lookups to improve performance.")
.define("chunkCaching", true);
} }
} }
} }

View file

@ -1,6 +1,8 @@
package com.jozufozu.flywheel.mixin; package com.jozufozu.flywheel.mixin;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@ -8,7 +10,10 @@ import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.jozufozu.flywheel.backend.Backend;
import net.minecraft.client.multiplayer.ClientChunkProvider; import net.minecraft.client.multiplayer.ClientChunkProvider;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraft.world.biome.BiomeContainer; import net.minecraft.world.biome.BiomeContainer;
@ -20,6 +25,9 @@ import net.minecraft.world.chunk.IChunk;
@Mixin(ClientChunkProvider.class) @Mixin(ClientChunkProvider.class)
public abstract class FastChunkProviderMixin extends AbstractChunkProvider { public abstract class FastChunkProviderMixin extends AbstractChunkProvider {
@Shadow
@Final
private ClientWorld level;
@Unique @Unique
private int lastX; private int lastX;
@Unique @Unique
@ -32,36 +40,52 @@ public abstract class FastChunkProviderMixin extends AbstractChunkProvider {
at = @At("HEAD"), at = @At("HEAD"),
cancellable = true) cancellable = true)
public void returnCachedChunk(int x, int z, ChunkStatus status, boolean create, CallbackInfoReturnable<IChunk> cir) { public void returnCachedChunk(int x, int z, ChunkStatus status, boolean create, CallbackInfoReturnable<IChunk> cir) {
if (status.isOrAfter(ChunkStatus.FULL) && lastChunk != null && x == lastX && z == lastZ) { if (Backend.getInstance().chunkCachingEnabled && status.isOrAfter(ChunkStatus.FULL)) {
synchronized (level) {
if (lastChunk != null && x == lastX && z == lastZ) {
cir.setReturnValue(lastChunk); cir.setReturnValue(lastChunk);
} }
} }
}
}
@Inject(method = "getChunk", @Inject(method = "getChunk",
at = @At("RETURN")) at = @At("RETURN"))
public void cacheChunk(int x, int z, ChunkStatus status, boolean create, CallbackInfoReturnable<IChunk> cir) { public void cacheChunk(int x, int z, ChunkStatus status, boolean create, CallbackInfoReturnable<IChunk> cir) {
if (status.isOrAfter(ChunkStatus.FULL)) { if (Backend.getInstance().chunkCachingEnabled && status.isOrAfter(ChunkStatus.FULL)) {
synchronized (level) {
lastChunk = cir.getReturnValue(); lastChunk = cir.getReturnValue();
lastX = x; lastX = x;
lastZ = z; lastZ = z;
} }
} }
}
@Inject(method = "drop", at = @At("HEAD")) @Inject(method = "drop", at = @At("HEAD"))
public void invalidateOnDrop(int x, int z, CallbackInfo ci) { public void invalidateOnDrop(int x, int z, CallbackInfo ci) {
if (x == lastX && z == lastZ) if (Backend.getInstance().chunkCachingEnabled) {
lastChunk = null; synchronized (level) {
if (x == lastX && z == lastZ) lastChunk = null;
}
}
} }
@Inject(method = "replaceWithPacketData", at = @At("HEAD")) @Inject(method = "replaceWithPacketData", at = @At("HEAD"))
public void invalidateOnPacket(int x, int z, BiomeContainer p_228313_3_, PacketBuffer p_228313_4_, CompoundNBT p_228313_5_, int p_228313_6_, boolean p_228313_7_, CallbackInfoReturnable<Chunk> cir) { public void invalidateOnPacket(int x, int z, BiomeContainer p_228313_3_, PacketBuffer p_228313_4_, CompoundNBT p_228313_5_, int p_228313_6_, boolean p_228313_7_, CallbackInfoReturnable<Chunk> cir) {
if (x == lastX && z == lastZ) if (Backend.getInstance().chunkCachingEnabled) {
lastChunk = null; synchronized (level) {
if (x == lastX && z == lastZ) lastChunk = null;
}
}
} }
@Redirect(method = "isTickingChunk", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientChunkProvider;hasChunk(II)Z")) @Redirect(method = "isTickingChunk", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientChunkProvider;hasChunk(II)Z"))
public boolean redirectTicking(ClientChunkProvider clientChunkProvider, int x, int z) { public boolean redirectTicking(ClientChunkProvider clientChunkProvider, int x, int z) {
if (Backend.getInstance().chunkCachingEnabled) {
synchronized (level) {
if (lastChunk != null && x == lastX && z == lastZ) return true; if (lastChunk != null && x == lastX && z == lastZ) return true;
}
}
return clientChunkProvider.hasChunk(x, z); return clientChunkProvider.hasChunk(x, z);
} }