Implement chunk forcing during rendering for 1.15

This commit is contained in:
Jonas Herzig
2020-04-02 14:15:05 +02:00
parent 4ea13051fd
commit c119392c92
6 changed files with 230 additions and 37 deletions

View File

@@ -16,7 +16,7 @@ import com.replaymod.render.mixin.MainWindowAccessor;
import static com.replaymod.core.versions.MCVer.getWindow; import static com.replaymod.core.versions.MCVer.getWindow;
//#endif //#endif
//#if MC>=10800 && MC<11500 //#if MC>=10800
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal; import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
//#endif //#endif
@@ -44,7 +44,7 @@ public class ScreenshotRenderer implements RenderInfo {
boolean hideGUIBefore = mc.options.hudHidden; boolean hideGUIBefore = mc.options.hudHidden;
mc.options.hudHidden = true; mc.options.hudHidden = true;
//#if MC>=10800 && MC<11500 //#if MC>=10800
ChunkLoadingRenderGlobal clrg = new ChunkLoadingRenderGlobal(mc.worldRenderer); ChunkLoadingRenderGlobal clrg = new ChunkLoadingRenderGlobal(mc.worldRenderer);
//#endif //#endif
@@ -56,7 +56,7 @@ public class ScreenshotRenderer implements RenderInfo {
new ScreenshotWriter(settings.getOutputFile())).run(); new ScreenshotWriter(settings.getOutputFile())).run();
} }
//#if MC>=10800 && MC<11500 //#if MC>=10800
clrg.uninstall(); clrg.uninstall();
//#endif //#endif

View File

@@ -1,19 +1,23 @@
// FIXME 1.15 chunk rendering has changed significantly, ignoring this feature for now //#if MC>=10800
//#if MC>=10800 && MC<11500
package com.replaymod.render.hooks; package com.replaymod.render.hooks;
import net.minecraft.client.render.WorldRenderer;
import java.lang.reflect.Field;
//#if MC>=11500
//#else
import com.replaymod.render.mixin.ChunkRenderDispatcherAccessor; import com.replaymod.render.mixin.ChunkRenderDispatcherAccessor;
import com.replaymod.render.mixin.WorldRendererAccessor; import com.replaymod.render.mixin.WorldRendererAccessor;
import com.replaymod.render.utils.JailingQueue; import com.replaymod.render.utils.JailingQueue;
import net.minecraft.client.render.chunk.BlockBufferBuilderStorage; import net.minecraft.client.render.chunk.BlockBufferBuilderStorage;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.render.chunk.ChunkBuilder; import net.minecraft.client.render.chunk.ChunkBuilder;
import net.minecraft.client.render.chunk.ChunkRenderTask; import net.minecraft.client.render.chunk.ChunkRenderTask;
import net.minecraft.client.render.chunk.ChunkRenderWorker; import net.minecraft.client.render.chunk.ChunkRenderWorker;
import net.minecraft.client.render.chunk.ChunkRenderer; import net.minecraft.client.render.chunk.ChunkRenderer;
import java.lang.reflect.Field;
import java.util.Iterator; import java.util.Iterator;
import static com.replaymod.core.versions.MCVer.*;
//#endif
//#if MC>=10904 //#if MC>=10904
import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.PriorityBlockingQueue;
@@ -21,8 +25,6 @@ import java.util.concurrent.PriorityBlockingQueue;
//$$ import java.util.concurrent.BlockingQueue; //$$ import java.util.concurrent.BlockingQueue;
//#endif //#endif
import static com.replaymod.core.versions.MCVer.*;
public class ChunkLoadingRenderGlobal { public class ChunkLoadingRenderGlobal {
//#if MC>=11400 //#if MC>=11400
@@ -30,6 +32,9 @@ public class ChunkLoadingRenderGlobal {
//#else //#else
//$$ private final RenderGlobal hooked; //$$ private final RenderGlobal hooked;
//#endif //#endif
//#if MC>=11500
//#else
private ChunkBuilder renderDispatcher; private ChunkBuilder renderDispatcher;
//#if MC>=11400 //#if MC>=11400
private JailingQueue<ChunkRenderTask> workerJailingQueue; private JailingQueue<ChunkRenderTask> workerJailingQueue;
@@ -38,8 +43,8 @@ public class ChunkLoadingRenderGlobal {
//#endif //#endif
private ChunkRenderWorkerAccessor renderWorker; private ChunkRenderWorkerAccessor renderWorker;
private int frame; private int frame;
//#endif
@SuppressWarnings("unchecked")
public ChunkLoadingRenderGlobal( public ChunkLoadingRenderGlobal(
//#if MC>=11400 //#if MC>=11400
WorldRenderer renderGlobal WorldRenderer renderGlobal
@@ -49,9 +54,29 @@ public class ChunkLoadingRenderGlobal {
) { ) {
this.hooked = renderGlobal; this.hooked = renderGlobal;
//#if MC>=11500
//$$ install();
//#else
setup(((WorldRendererAccessor) renderGlobal).getRenderDispatcher()); setup(((WorldRendererAccessor) renderGlobal).getRenderDispatcher());
install();
//#endif
} }
private void install() {
try {
//#if MC>=11400
Field hookField = WorldRenderer.class.getField("replayModRender_hook");
//#else
//$$ Field hookField = RenderGlobal.class.getField("replayModRender_hook");
//#endif
hookField.set(hooked, this);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}
//#if MC>=11500
//#else
public void updateRenderDispatcher(ChunkBuilder renderDispatcher) { public void updateRenderDispatcher(ChunkBuilder renderDispatcher) {
if (this.renderDispatcher != null) { if (this.renderDispatcher != null) {
workerJailingQueue.freeAll(); workerJailingQueue.freeAll();
@@ -98,17 +123,6 @@ public class ChunkLoadingRenderGlobal {
workerJailingQueue.jail(workerThreads); workerJailingQueue.jail(workerThreads);
renderDispatcherAcc.setQueueChunkUpdates(queueChunkUpdates); renderDispatcherAcc.setQueueChunkUpdates(queueChunkUpdates);
try {
//#if MC>=11400
Field hookField = WorldRenderer.class.getField("replayModRender_hook");
//#else
//$$ Field hookField = RenderGlobal.class.getField("replayModRender_hook");
//#endif
hookField.set(hooked, this);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
} }
public void updateChunks() { public void updateChunks() {
@@ -143,8 +157,15 @@ public class ChunkLoadingRenderGlobal {
} }
} }
public int nextFrameId() {
return frame++;
}
//#endif
public void uninstall() { public void uninstall() {
//#if MC<11500
workerJailingQueue.freeAll(); workerJailingQueue.freeAll();
//#endif
try { try {
//#if MC>=11400 //#if MC>=11400
@@ -158,8 +179,10 @@ public class ChunkLoadingRenderGlobal {
} }
} }
public int nextFrameId() { //#if MC>=11500
return frame++; //$$ public interface IBlockOnChunkRebuilds {
} //$$ boolean uploadEverythingBlocking();
//$$ }
//#endif
} }
//#endif //#endif

View File

@@ -0,0 +1,111 @@
package com.replaymod.render.mixin;
//#if MC>=11500
//$$ import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
//$$ import net.minecraft.client.render.chunk.BlockBufferBuilderStorage;
//$$ import net.minecraft.client.render.chunk.ChunkBuilder;
//$$ import net.minecraft.util.thread.TaskExecutor;
//$$ import org.spongepowered.asm.mixin.Final;
//$$ import org.spongepowered.asm.mixin.Mixin;
//$$ import org.spongepowered.asm.mixin.Shadow;
//$$ import org.spongepowered.asm.mixin.injection.At;
//$$ import org.spongepowered.asm.mixin.injection.Inject;
//$$ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//$$ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
//$$
//$$ import java.util.Queue;
//$$ import java.util.concurrent.CompletableFuture;
//$$ import java.util.concurrent.locks.Condition;
//$$ import java.util.concurrent.locks.Lock;
//$$ import java.util.concurrent.locks.ReentrantLock;
//$$
//$$ @Mixin(ChunkBuilder.class)
//$$ public abstract class Mixin_BlockOnChunkRebuilds implements ChunkLoadingRenderGlobal.IBlockOnChunkRebuilds {
//$$ @Shadow @Final private Queue<BlockBufferBuilderStorage> threadBuffers;
//$$
//$$ @Shadow public abstract boolean upload();
//$$
//$$ @Shadow @Final private TaskExecutor<Runnable> mailbox;
//$$
//$$ @Shadow protected abstract void scheduleRunTasks();
//$$
//$$ @Shadow @Final private Queue<Runnable> uploadQueue;
//$$ private final Lock waitingForWorkLock = new ReentrantLock();
//$$ private final Condition newWork = waitingForWorkLock.newCondition();
//$$ private volatile boolean allDone;
//$$
//$$ private int totalBufferCount;
//$$
//$$ @Inject(method = "<init>", at = @At("RETURN"))
//$$ private void rememberTotalThreads(CallbackInfo ci) {
//$$ this.totalBufferCount = this.threadBuffers.size();
//$$ }
//$$
//$$ @Inject(method = "scheduleRunTasks", at = @At("RETURN"))
//$$ private void notifyMainThreadIfEverythingIsDone(CallbackInfo ci) {
//$$ if (this.threadBuffers.size() == this.totalBufferCount) {
//$$ // Looks like we're done, better notify the main thread in case the previous task didn't generate an upload
//$$ this.waitingForWorkLock.lock();
//$$ try {
//$$ this.allDone = true;
//$$ this.newWork.signalAll();
//$$ } finally {
//$$ this.waitingForWorkLock.unlock();
//$$ }
//$$ } else {
//$$ this.allDone = false;
//$$ }
//$$ }
//$$
//$$ @Inject(method = "scheduleUpload", at = @At("RETURN"))
//$$ private void notifyMainThreadOfNewUpload(CallbackInfoReturnable<CompletableFuture<Void>> ci) {
//$$ this.waitingForWorkLock.lock();
//$$ try {
//$$ this.newWork.signal();
//$$ } finally {
//$$ this.waitingForWorkLock.unlock();
//$$ }
//$$ }
//$$
//$$ private boolean waitForMainThreadWork() {
//$$ boolean allDone = this.mailbox.<Boolean>ask(reply -> () -> {
//$$ scheduleRunTasks();
//$$ reply.send(this.threadBuffers.size() == this.totalBufferCount);
//$$ }).join();
//$$
//$$ if (allDone) {
//$$ return true;
//$$ } else {
//$$ this.waitingForWorkLock.lock();
//$$ try {
//$$ while (true) {
//$$ if (this.allDone) {
//$$ return true;
//$$ } else if (!this.uploadQueue.isEmpty()) {
//$$ return false;
//$$ } else {
//$$ this.newWork.awaitUninterruptibly();
//$$ }
//$$ }
//$$ } finally {
//$$ this.waitingForWorkLock.unlock();
//$$ }
//$$ }
//$$ }
//$$
//$$ @Override
//$$ public boolean uploadEverythingBlocking() {
//$$ boolean anything = false;
//$$
//$$ boolean allChunksBuilt;
//$$ do {
//$$ allChunksBuilt = waitForMainThreadWork();
//$$ while (upload()) {
//$$ anything = true;
//$$ }
//$$ } while (!allChunksBuilt);
//$$
//$$ return anything;
//$$ }
//$$ }
//#endif

View File

@@ -1,17 +1,24 @@
//#if MC>=10800 && MC<11500
package com.replaymod.render.mixin; package com.replaymod.render.mixin;
//#if MC>=10800
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal; import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.Frustum;
import net.minecraft.client.render.chunk.ChunkBuilder; import net.minecraft.client.render.chunk.ChunkBuilder;
import net.minecraft.client.render.VisibleRegion;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#if MC>=11400 //#if MC>=11500
//$$ import java.util.Set;
//#else
import net.minecraft.client.render.VisibleRegion;
import net.minecraft.entity.Entity;
//#endif
//#if MC>=11400 && MC<11500
import net.minecraft.client.render.Camera; import net.minecraft.client.render.Camera;
//#endif //#endif
@@ -32,8 +39,57 @@ import net.minecraft.client.render.WorldRenderer;
//#else //#else
//$$ @Mixin(RenderGlobal.class) //$$ @Mixin(RenderGlobal.class)
//#endif //#endif
public abstract class MixinRenderGlobal { public abstract class Mixin_ForceChunkLoading {
public ChunkLoadingRenderGlobal replayModRender_hook; public ChunkLoadingRenderGlobal replayModRender_hook;
//#if MC>=11500
//$$ @Shadow private Set<ChunkBuilder.BuiltChunk> chunksToRebuild;
//$$
//$$ @Shadow private ChunkBuilder chunkBuilder;
//$$
//$$ @Shadow private boolean needsTerrainUpdate;
//$$
//$$ @Shadow public abstract void scheduleTerrainUpdate();
//$$
//$$ @Shadow protected abstract void setupTerrain(Camera camera_1, Frustum frustum_1, boolean boolean_1, int int_1, boolean boolean_2);
//$$
//$$ @Shadow private int frame;
//$$
//$$ private boolean passThrough;
//$$ @Inject(method = "setupTerrain", at = @At("HEAD"), cancellable = true)
//$$ private void forceAllChunks(Camera camera_1, Frustum frustum_1, boolean boolean_1, int int_1, boolean boolean_2, CallbackInfo ci) {
//$$ if (replayModRender_hook == null) {
//$$ return;
//$$ }
//$$ if (passThrough) {
//$$ return;
//$$ }
//$$ ci.cancel();
//$$
//$$ passThrough = true;
//$$ try {
//$$ do {
//$$ // Determine which chunks shall be visible
//$$ setupTerrain(camera_1, frustum_1, boolean_1, frame++, boolean_2);
//$$
//$$ // Schedule all chunks which need rebuilding (we schedule even important rebuilds because we wait for
//$$ // all of them anyway and this way we can take advantage of threading)
//$$ for (ChunkBuilder.BuiltChunk builtChunk : this.chunksToRebuild) {
//$$ builtChunk.scheduleRebuild(this.chunkBuilder);
//$$ builtChunk.cancelRebuild();
//$$ }
//$$ this.chunksToRebuild.clear();
//$$
//$$ // Upload all chunks
//$$ this.needsTerrainUpdate |= ((ChunkLoadingRenderGlobal.IBlockOnChunkRebuilds) this.chunkBuilder).uploadEverythingBlocking();
//$$
//$$ // Repeat until no more updates are needed
//$$ } while (this.needsTerrainUpdate);
//$$ } finally {
//$$ passThrough = false;
//$$ }
//$$ }
//#else
private boolean replayModRender_passThroughSetupTerrain; private boolean replayModRender_passThroughSetupTerrain;
@Shadow @Shadow
@@ -132,5 +188,6 @@ public abstract class MixinRenderGlobal {
replayModRender_hook.updateRenderDispatcher(this.chunkBuilder); replayModRender_hook.updateRenderDispatcher(this.chunkBuilder);
} }
} }
//#endif
} }
//#endif //#endif

View File

@@ -49,9 +49,7 @@ import org.lwjgl.glfw.GLFW;
//#endif //#endif
//#if MC>=10800 //#if MC>=10800
//#if MC<11500
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal; import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
//#endif
import static com.mojang.blaze3d.platform.GlStateManager.*; import static com.mojang.blaze3d.platform.GlStateManager.*;
//#else //#else
//$$ import com.replaymod.replay.gui.screen.GuiOpeningReplay; //$$ import com.replaymod.replay.gui.screen.GuiOpeningReplay;
@@ -88,7 +86,7 @@ public class VideoRenderer implements RenderInfo {
private TimelinePlayer timelinePlayer; private TimelinePlayer timelinePlayer;
private Future<Void> timelinePlayerFuture; private Future<Void> timelinePlayerFuture;
//#if MC>=10800 && MC<11500 //#if MC>=10800
private ChunkLoadingRenderGlobal chunkLoadingRenderGlobal; private ChunkLoadingRenderGlobal chunkLoadingRenderGlobal;
//#endif //#endif
//#if MC<10800 //#if MC<10800
@@ -314,7 +312,7 @@ public class VideoRenderer implements RenderInfo {
//$$ gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight()); //$$ gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight());
//#endif //#endif
//#if MC>=10800 && MC<11500 //#if MC>=10800
chunkLoadingRenderGlobal = new ChunkLoadingRenderGlobal(mc.worldRenderer); chunkLoadingRenderGlobal = new ChunkLoadingRenderGlobal(mc.worldRenderer);
//#endif //#endif
@@ -351,7 +349,7 @@ public class VideoRenderer implements RenderInfo {
mc.options.setSoundVolume(entry.getKey(), entry.getValue()); mc.options.setSoundVolume(entry.getKey(), entry.getValue());
} }
mc.openScreen(null); mc.openScreen(null);
//#if MC>=10800 && MC<11500 //#if MC>=10800
if (chunkLoadingRenderGlobal != null) { if (chunkLoadingRenderGlobal != null) {
chunkLoadingRenderGlobal.uninstall(); chunkLoadingRenderGlobal.uninstall();
} }

View File

@@ -8,9 +8,13 @@
"Mixin_ChromaKeyForceSky", "Mixin_ChromaKeyForceSky",
"Mixin_CubicRotation", "Mixin_CubicRotation",
"Mixin_SkipBlockOutlinesDuringRender", "Mixin_SkipBlockOutlinesDuringRender",
//#if MC>=10800 && MC<11500 //#if MC>=10800
//#if MC>=11500
//$$ "Mixin_BlockOnChunkRebuilds",
//#else
"ChunkRenderDispatcherAccessor", "ChunkRenderDispatcherAccessor",
"MixinChunkRenderWorker", //#endif
"Mixin_ForceChunkLoading",
//#endif //#endif
//#if MC>=11400 //#if MC>=11400
"MainWindowAccessor", "MainWindowAccessor",